We found a bug this morning that had been silently breaking agent payment requests for weeks. It wasn't a code bug. It wasn't a protocol bug. It was a CDN configuration default that strips a standard HTTP header before it ever reaches the application server.
Here's the full debugging story, why it matters for every x402 implementation, and what you should do about it.
Authorization header from proxied HTTP requests by default. If your agent payment protocol relies on Authorization: Bearer <wallet>, your agents are silently failing — getting "trial exhausted" or "registration required" responses instead of spending their credits. Use x-wallet header or ?wallet= query parameter instead.
We'd been tracking a persistent conversion gap. Users register, receive 500 free credits, but only 2.2% of issued credits ever get spent. At first glance, this looked like a product problem — users sign up but don't come back. But when we dug deeper, we noticed something odd.
When we tested credit spending from our own infrastructure (direct localhost), everything worked. When we tested from an external IP (through Cloudflare's edge network), Authorization: Bearer requests returned trial-exhaustion responses instead of spending credits.
The same wallet, the same endpoint, the same request — different results depending on whether the request went through Cloudflare.
We started with a systematic comparison of auth methods:
# Test 1: Query parameter (through Cloudflare)
curl -s "https://minia2a.uk/x402/ping?wallet=0xf16..."
→ {"ok":true, "trialsRemaining":264} ✅
# Test 2: Custom header (through Cloudflare)
curl -s "https://minia2a.uk/x402/ping" -H "x-wallet: 0xf16..."
→ {"ok":true, "trialsRemaining":264} ✅
# Test 3: Bearer auth (through Cloudflare)
curl -s "https://minia2a.uk/x402/ping" -H "Authorization: Bearer 0xf16..."
→ {"_trial":true, "message":"15 free trials used"} ❌
Tests 1 and 2 worked correctly — the wallet was recognized and credits were deducted. Test 3 fell through to IP-based trial counting, which had already been exhausted. This strongly suggested the wallet address wasn't being extracted from the Authorization header.
But our code was correct:
function extractWallet(req) {
const qw = req.query?.wallet || '';
const hw = req.headers?.['x-wallet'] || req.headers?.['x-address'] || '';
const auth = req.headers?.['authorization'] || '';
const m = auth.match(/^Bearer\s+(0x[0-9a-fA-F]{40})\s*$/);
const aw = m ? m[1] : '';
return (qw || hw || aw).toLowerCase();
}
The regex was correct. The logic was sound. Logging confirmed the code was being reached. But req.headers['authorization'] was always empty for external requests.
Then we ran the killer test — bypass Cloudflare entirely:
# Direct to origin (localhost, no CDN)
$ ssh server "curl -s 'http://127.0.0.1/x402/ping' \
-H 'Authorization: Bearer 0xf16...'"
→ {"ok":true} ✅
Direct origin: Bearer works. Through Cloudflare: Bearer silently disappears.
Cloudflare applies a set of "Managed Transforms" to HTTP requests passing through its edge network. One of these transforms removes the Authorization header from requests to origin servers.
This is a security feature, not a bug. Cloudflare's rationale is sensible for most web applications:
But for agent payment protocols, this default creates a silent failure mode. The request doesn't get a 401 or 403 — it gets a 200 with a misleading "register for free credits" message. The agent thinks it's unauthenticated when in reality its credential was simply stripped in transit.
| Auth Method | Through Cloudflare | Direct Origin |
|---|---|---|
?wallet=0x... | Works | Works |
x-wallet header | Works | Works |
x-address header | Works | Works |
Authorization: Bearer | Stripped by CF | Works |
The x402 ecosystem runs heavily on Cloudflare. The x402 Foundation itself uses Cloudflare. Many facilitators and service providers do too. The Authorization: Bearer pattern is the most natural authentication method for developers — it's what every HTTP client library defaults to, what every API tutorial teaches, and what AI coding assistants generate.
When a developer writes:
// Natural, idiomatic, every framework supports this
fetch('https://agent-api.example.com/x402/data', {
headers: { 'Authorization': `Bearer ${wallet}` }
})
They are walking into a silent failure if the API is behind Cloudflare. The request reaches the server, but the Authorization header doesn't.
Document and support multiple auth methods. Don't rely exclusively on the Authorization header:
# Method 1: Query parameter (simplest, always works)
?wallet=0xYourAddress...
# Method 2: Custom header (bypasses CDN stripping)
x-wallet: 0xYourAddress...
# Method 3: Authorization: Bearer (works direct, may fail through CDNs)
Authorization: Bearer 0xYourAddress...
If you have Cloudflare dashboard access, you can disable the Authorization header stripping with a Transform Rule. But many teams don't have this access, or don't know they need it. The safer approach is to support methods that work regardless of CDN configuration.
When generating HTTP requests for agent payments, prefer custom headers (x-wallet) over Authorization: Bearer. Custom headers pass through CDNs transparently. This is a small change that eliminates an entire class of silent failures.
On the minia2a marketplace:
?wallet= and x-wallet header over Authorization: Bearer?wallet= by defaultextractWallet function still parses Bearer tokens for direct-origin access, but with a comment documenting the Cloudflare limitationX-Register and X-Register-Free headersInfrastructure defaults shape protocol adoption in ways that protocol designers rarely anticipate. The x402 protocol spec doesn't mention CDN header stripping. The HTTP/1.1 and HTTP/2 specs say Authorization is a standard header. But the operational reality — Cloudflare's defaults, which affect ~20% of global web traffic — creates a de facto constraint on which authentication patterns actually work.
This is a useful pattern for anyone building on x402: test through the full infrastructure stack, not just your application code. The protocol layer, the CDN layer, and the application layer each have their own assumptions about what headers mean and which ones should survive. When those assumptions conflict, the result is silent failures that are hard to debug and harder to measure.
For minia2a specifically, this discovery explains part of the credit utilization gap we've been tracking. Every agent that tried Authorization: Bearer and got a "trials exhausted" response was a lost credit-spending session. We can't retroactively fix those, but we can ensure future agents use the right auth pattern from the start.