Traefik v3 middleware that gates HTTP routes behind on-chain stablecoin micropayments using the x402 protocol.
Put any HTTP service behind a paywall without touching its source code. A client that cannot pay gets a standard 402 Payment Required with a machine-readable PaymentRequirements body. A client that can pay signs the payment off-chain, retries the request, and the middleware handles verify-and-settle against a remote facilitator — no custom backend logic required.
The default facilitator is Coinbase's public https://x402.org/facilitator, free to use on Base Sepolia. USDC on Base / Base Sepolia is the primary supported asset.
The plugin has zero non-stdlib runtime dependencies and is interpreted by Yaegi at Traefik startup. It never touches private keys or constructs on-chain transactions — all cryptographic verification and settlement is delegated to the facilitator over plain HTTPS JSON.
Client Traefik (x402 middleware) Facilitator Upstream
| | | |
|--- GET /api ---------------> | | |
|<-- 402 PaymentRequirements --| | |
| | | |
|--- GET /api | | |
| X-PAYMENT: <base64> ----> | | |
| |--- POST /verify -----------> | |
| |<-- { isValid: true } ------- | |
| | | |
| |--- forward request ----------------------------->|
| |<-- 200 body (buffered) --------------------------|
| | | |
| |--- POST /settle ------------>| |
| |<-- { success: true } --------| |
| | | |
|<-- 200 body | | |
| X-PAYMENT-RESPONSE: <b64> | | |
- No
X-PAYMENTheader — middleware returns402with aPaymentRequirementsJSON body. No upstream call is made. X-PAYMENTpresent — middleware base64-decodes the header, JSON-parses thePaymentPayload, and POSTs to{facilitatorURL}/verify.- Verify fails — middleware returns
402with the failure reason. No upstream call is made. - Verify succeeds — middleware forwards the request to the upstream and buffers the full response body.
- Upstream returns 2xx — middleware POSTs to
{facilitatorURL}/settle. On success, addsX-PAYMENT-RESPONSE(base64 JSON containing the on-chain tx hash) to the response. If settle fails, the 2xx is replaced with a402. - Upstream returns non-2xx — settle is skipped; the buffered error response is forwarded as-is.
Streaming responses are not supported — the upstream response is always fully buffered before settle is attempted.
Available once the plugin is listed at https://plugins.traefik.io.
In Traefik's static configuration:
# traefik.yml
experimental:
plugins:
x402:
moduleName: github.com/albttx/traefik-plugin-x402
version: v0.1.0 # replace with the latest tagged releaseMount the plugin source under Traefik's plugin directory at the path matching its module name:
/plugins-local/
src/
github.com/
albttx/
traefik-plugin-x402/
plugin.go
go.mod
.traefik.yml
internal/
x402/
Then in static config:
# traefik.yml
experimental:
localPlugins:
x402:
moduleName: github.com/albttx/traefik-plugin-x402| Name | Required | Default | Description |
|---|---|---|---|
payTo |
yes | — | Recipient wallet address (EVM checksum address) |
network |
yes | — | Network identifier, e.g. base, base-sepolia |
asset |
yes | — | ERC-20 contract address of the payment token |
amount |
yes | — | Amount in atomic units as a decimal string. USDC has 6 decimals, so "1000" = $0.001 |
assetName |
yes¹ | "" |
EIP-712 domain name for the asset. For Circle USDC: USDC. |
assetVersion |
yes¹ | "" |
EIP-712 domain version for the asset. For Circle USDC: 2. |
description |
no | "" |
Human-readable description included in the PaymentRequirements |
mimeType |
no | application/json |
MIME type of the protected resource |
resource |
no | full request URL | Override the resource URL advertised in PaymentRequirements |
scheme |
no | exact |
Payment scheme. The public facilitator implements exact. |
facilitatorURL |
no | https://x402.org/facilitator |
Base URL of the x402 facilitator |
maxTimeoutSeconds |
no | 60 |
Maximum time (seconds) the client has to complete the payment |
¹ assetName / assetVersion are required by any facilitator implementing the exact scheme on EVM — they're embedded in PaymentRequirements.extra so the client signs against the correct EIP-712 domain. Omitting them produces an invalid_exact_evm_missing_eip712_domain verify error. For non-USDC tokens, read the contract's name() and check the source for the EIP-712 version constant.
labels:
- "traefik.enable=true"
- "traefik.http.routers.paid.rule=Host(`api.example.com`)"
- "traefik.http.routers.paid.middlewares=x402-pay"
- "traefik.http.middlewares.x402-pay.plugin.x402.payTo=0xYourWallet"
- "traefik.http.middlewares.x402-pay.plugin.x402.network=base"
- "traefik.http.middlewares.x402-pay.plugin.x402.asset=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
- "traefik.http.middlewares.x402-pay.plugin.x402.amount=1000"
- "traefik.http.middlewares.x402-pay.plugin.x402.assetName=USDC"
- "traefik.http.middlewares.x402-pay.plugin.x402.assetVersion=2"# dynamic.yml
http:
middlewares:
x402-pay:
plugin:
x402:
payTo: "0xYourWallet"
network: "base"
asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" # USDC on Base mainnet
amount: "1000"
assetName: "USDC"
assetVersion: "2"
description: "API access"
routers:
paid:
rule: "Host(`api.example.com`)"
entryPoints: [web]
service: my-upstream@file
middlewares: [x402-pay@file]| Dimension | Supported |
|---|---|
| Traefik | v3.7+ (older versions fail against Docker Engine 29's MinAPIVersion=1.44) |
| Networks (public facilitator) | base, base-sepolia |
| Default asset | USDC |
| Plugin runtime dependencies | none (stdlib only) |
A one-command Docker Compose demo with Traefik + a dummy upstream + the plugin loaded via Yaegi is included. See docs/dev.md for the quickstart, the unit / end-to-end test workflow, and troubleshooting.
- Pluggable facilitator selection per-route
- Prometheus metrics (verify/settle latency, failure rates)
- Structured JSON logging
- Response streaming (currently buffered)
- Payment caching via cookie or JWT to avoid re-verification for the same payer across requests
- Solana and Stellar network support once public facilitators are available
Pull requests welcome. See docs/dev.md for setup, test commands, and dev environment details. Open an issue first to discuss non-trivial changes.
MIT — see LICENSE.
Implements the x402 protocol designed and specified by Coinbase. The plugin system is provided by Traefik.