Guide · updated 2026-10-12 · about 3 minutes

Why can’t my browser x402 client read the PAYMENT-REQUIRED header?

Quick answer: browsers hide non-standard response headers from cross-origin scripts. Send Access-Control-Expose-Headers: PAYMENT-REQUIRED, PAYMENT-RESPONSE on your 402 and paid responses, and answer the OPTIONS preflight with Access-Control-Allow-Headers that includes PAYMENT-SIGNATURE.

What the spec puts in headers

In the x402 v2 HTTP transport, the server sends the payment terms base64-encoded in PAYMENT-REQUIRED with the 402. The client retries with PAYMENT-SIGNATURE, and the server confirms with PAYMENT-RESPONSE. PAYMENT-REQUIRED and PAYMENT-RESPONSE are response headers that aren’t CORS-safelisted, so a script on another origin sees the 402 status but gets null for them unless you list them in Access-Control-Expose-Headers. PAYMENT-SIGNATURE is a request header: it goes in Access-Control-Allow-Headers, not the expose list.

Minimal Worker headers

const CORS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'PAYMENT-SIGNATURE, X-PAYMENT, Content-Type',
  'Access-Control-Expose-Headers': 'PAYMENT-REQUIRED, PAYMENT-RESPONSE',
};
// OPTIONS -> new Response(null, { status: 204, headers: CORS })
// add CORS to the 402 and to the paid 200

Test it with curl

# the 402 exposes the terms
curl -s -D - -o /dev/null "$URL" | grep -i '^access-control-expose-headers'
# the preflight allows the payment header
curl -s -D - -o /dev/null -X OPTIONS "$URL" \
  -H 'Origin: https://example.com' \
  -H 'Access-Control-Request-Method: GET' \
  -H 'Access-Control-Request-Headers: payment-signature' | grep -i '^access-control-allow-headers'

Or paste the URL into the free x402 endpoint checker, which warns when the 402 has no CORS headers or doesn’t expose PAYMENT-REQUIRED. It doesn’t send a preflight, so run the second curl command for that.

FAQ

Which headers does x402 v2 use over HTTP?

PAYMENT-REQUIRED (server to client, with the 402), PAYMENT-SIGNATURE (client to server) and PAYMENT-RESPONSE (server to client, with the settlement result). All three carry base64-encoded JSON.

What about X-PAYMENT?

X-PAYMENT is the older v1 request header. Some servers accept it alongside PAYMENT-SIGNATURE for older clients, so allow both in Access-Control-Allow-Headers if you support both.

Do server-side agents need CORS?

No. CORS only applies to scripts running in a browser. Server-side clients and agents read every header regardless.

Sources: the x402 v2 HTTP transport spec, Cloudflare Agents x402 docs and MDN on Access-Control-Expose-Headers. More checks: how to test an x402 endpoint with curl.

Want a store with this already set up? The x402 Seller Kit is a tested Cloudflare Worker template whose 402 already exposes PAYMENT-REQUIRED to browsers. The README is free to read.

Get the kit · $29