A Cloudflare Worker that sits in front of your website and enforces HTTP 402 Payment Required for AI agents. Humans and crawlers like Googlebot browse free; AI bots (GPTBot, ClaudeBot, etc.) must present a signed payment intent to access paid paths.
Philosophy: “Humans browse free; Bots pay fee.”
- Bot detection — Requests are checked by
User-Agent. Payable bots (e.g. GPTBot, ClaudeBot, CCBot, Anthropic) are flagged. - Path-based pricing — Rules in
src/config/rules.tsmap URL paths to price, token (a ticker likeUSDCfrom theTOKENSmap), and strategy (PAY,FREE, orBLOCK). First matching rule wins; no match usesDEFAULT_PRICEandTOKEN_TICKERfrom env. Only stablecoins with a configured contract address (e.g. USDC on Base) are accepted. - 402 challenge — For payable paths, the worker requires an
Authorization: x402 <payload>.<signature>header. The signature is EIP-712 (Gatekeeper Protocol, AccessGrant). The signed message includes the token contract address (not the ticker) so only the official stablecoin is accepted. If missing or invalid, the response is 402 withx402-price,x402-recipient,x402-token, andx402-token-address. - Proxy — When the request is allowed (human, free path, or valid payment), it is forwarded to your origin and the response is streamed back.
- Runtime: Cloudflare Workers
- Framework: Hono
- Language: TypeScript
- Crypto: viem (EIP-712 signature verification)
| Variable | Description |
|---|---|
ORIGIN_URL |
Your backend or site (e.g. https://my-site.com) |
PAYMENT_ADDRESS |
EVM address that receives payments |
DEFAULT_PRICE |
Default price per request (e.g. "0.05") |
TOKEN_TICKER |
Default currency ticker; must be a key in TOKENS in src/config/rules.ts (e.g. "USDC") |
MODE |
PROTECT (enforce 402) or AUDIT (log only, never block) |
CHAIN_ID |
Chain for EIP-712 (default 8453 for Base) |
Set these in wrangler.toml under [vars] or via Cloudflare secrets. For local dev, use .dev.vars (gitignored).
A TOKENS map defines allowed stablecoins by ticker → contract address (e.g. USDC on Base). Only these contracts are accepted; agents must sign the exact address to prevent fake or wrong-token payments.
Pricing and access are controlled by an ordered list of rules. Each rule has:
| Field | Description |
|---|---|
pattern |
Regex string matched against the request path (e.g. ^/blog/.*) |
price |
Price for this path (e.g. "0.05") |
token |
Ticker that must be a key in TOKENS (e.g. "USDC") |
strategy |
PAY — require valid x402 payment; FREE — allow bots without payment; BLOCK — require payment (same as PAY for now) |
The worker evaluates rules in array order and uses the first rule whose pattern matches the path. If no rule matches, it falls back to DEFAULT_PRICE and TOKEN_TICKER from env (which must be a key in TOKENS).
Example:
export const TOKENS = {
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base mainnet
};
export const RULES: PricingRule[] = [
{ pattern: "^/research/premium/.*", price: "1.00", token: "USDC", strategy: "PAY" },
{ pattern: "^/blog/.*", price: "0.05", token: "USDC", strategy: "PAY" },
{ pattern: "^/$|^/about", price: "0", token: "USDC", strategy: "FREE" },
];Put more specific patterns first so they take precedence over broader ones.
npm install
npm run devPoint your client at the URL Wrangler prints (e.g. http://localhost:8787). Set ORIGIN_URL in wrangler.toml or .dev.vars to the site you want to proxy to.
- Cloudflare: Dashboard → My Profile → API Tokens → Create token (e.g. “Edit Cloudflare Workers”).
- GitHub: Repo → Settings → Secrets and variables → Actions. Add
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_ID. - Config: Set
ORIGIN_URL,PAYMENT_ADDRESS, and other vars inwrangler.tomlor as Worker secrets. - Push:
git push origin main— the.github/workflows/deploy.ymlworkflow deploys with Wrangler. - DNS: In Cloudflare, add a route or CNAME so your domain is handled by this Worker.
When a request returns 402, the response includes:
x402-price— Required amount (e.g.0.05)x402-recipient— Recipient addressx402-token— Token ticker (e.g.USDC)x402-token-address— Token contract address to use in the signed message (e.g. USDC on Base)
Sign an EIP-712 AccessGrant (domain: “Gatekeeper Protocol”, version “1.0”, chainId e.g. 8453 for Base, verifyingContract = recipient) with fields: visitor (your address), url (exact request URL), price, tokenAddress (the token contract address, e.g. from x402-token-address), and nonce (timestamp in ms). Encode the payload as base64 and the signature in hex, then send:
Authorization: x402 <base64(payload)>.<signature>