Skip to main content
ForgeMeshPay-per-Crawl for PHP · v0.1.0 · free, open source

Charge AI crawlers in Laravel, Drupal or plain PHP

A framework-free PHP core with a Laravel middleware package and a Drupal 10/11 module on top. WordPress has its own plugin.

Works with: Laravel 10+, Drupal 10.1+ / 11, Symfony, Slim or plain PHP via the core

Install

composer require forgemesh/pay-per-crawl-laravel   # or forgemesh/pay-per-crawl-drupal

Add the gate

Laravel .env (middleware joins the web group automatically).env
PAY_PER_CRAWL_MODE=monitor
PAY_PER_CRAWL_PAY_TO=0xYourWalletOnBase
PAY_PER_CRAWL_PRICE_USD=0.01
# php artisan migrate, then: php artisan pay-per-crawl:stats --days=7
Drupal
composer require forgemesh/pay-per-crawl-drupal
drush pm:install pay_per_crawl
# then /admin/config/services/pay-per-crawl: wallet, price, Gate
Plain PHP with the core
use ForgeMesh\PayPerCrawl\{Gate, Request, NativeHttpClient};

$gate = new Gate(
    settings: ['mode' => 'gate', 'pay_to' => '0xYourWalletOnBase', 'price_usd' => 0.01, 'mpp_secret' => 'long-random-string'],
    cache: $yourCache, http: new NativeHttpClient(),
);
$result = $gate->handle(new Request($method, $uri, $absoluteUrl, $headers, $remoteIp));
if ($result->respond) { /* send $result->status, $result->headers, $result->body and stop */ }
// else render the page and add $result->passHeaders

Turn it on

  1. 1composer require the Laravel or Drupal package. Laravel: php artisan migrate. Drupal: drush pm:install pay_per_crawl.
  2. 2Enter your wallet (Laravel .env, Drupal config form). Both start in monitor mode.
  3. 3Laravel: php artisan pay-per-crawl:stats. Drupal: /admin/reports/pay-per-crawl. Then switch to gate.

Settings

modemonitor (default) or gate. Without a valid wallet, gate stays monitor.
pay_toYour 0x wallet on Base.
price_usdPer page, default 0.01, minimum 0.005.
facilitator_url / facilitator_authAny x402 facilitator supporting Base exact. Default PayAI.
class_policy / bot_overridesfree, paid or block per class and per crawler.
allow_pathsPrefixes never gated, matched on a segment boundary.
trusted_ip_headerOnly a header your proxy overwrites or appends. Right-most entry used. Never set it if the origin is reachable directly.
mpp_secretSecret for MPP challenge signing and the daily IP hash.
cache_store (Laravel)Use redis, database or file for the replay lock. The array store does not survive between requests.

Things to know

  • Drupal gates as an http_middleware at priority 250, ahead of the page cache. A kernel.request subscriber would never see cached pages and bots would read them free.
  • Laravel: public/llms.txt or public/robots.txt files shadow the routes because the web server serves them first.
  • PHP rewrites the status to 401 when WWW-Authenticate is sent; the core sends the 402 after it. Keep that order in your own adapter.
  • Behind Cloudflare, set AI Crawl Control and any "Block AI bots" rule to Allow for the crawlers you want to charge. A crawler blocked at the edge never reaches the gate and never sees the 402.
  • Humans never see a 402. Search engines are verified by reverse DNS; a request that only claims to be Googlebot is charged.
  • Start in monitor mode. It logs every bot with what it would have paid and never blocks. Switch to gate when the numbers make sense.

Proof it settles

Real settlements on Base mainnet through the PayAI facilitator, $0.01 each, October 2026:

Other stacks

WordPressNext.jsVercelCloudflare WorkersNodenginxPythonGo