The Test: QUERY vs. Real Infrastructure

RFC 10008 shipped in June 2026, introducing HTTP QUERY—a safe, idempotent, cacheable method that carries its query in the body, like POST but with GET semantics. Within days, explainers and threat models appeared, but nobody actually fired a QUERY request at real infrastructure. So I did.

I sent one QUERY request through ten production edges: CloudFront, Akamai, Google Cloud CDN, Netlify, Bunny, Vercel, nginx, HAProxy, Fastly, Cloudflare, and Fly. The results were stark:

  • CloudFront: 403 Invalid method. No config fixes it; allowed methods are a closed set of seven verbs.
  • Akamai (co-authored RFC 10008!): 400, treating QUERY as an unparseable token.
  • Google, Netlify, Bunny: 405.
  • Vercel: 403 with bot mitigation challenge.
  • nginx, HAProxy, Fastly, Cloudflare, Fly: forwarded QUERY to origin.

The pattern: "smart" managed platforms with hardcoded method lists block QUERY; "dumb" configurable proxies let it through. The more a layer "understands" HTTP methods, the more likely it is to break the newest one.

Deeper into the Stack: Browsers, Clients, and Libraries

I tested cross-origin QUERY in Chromium, Firefox, and WebKit. It's not CORS-safelisted, so it preflights. If your server responds with Access-Control-Allow-Methods: GET, POST (which a huge share of the internet hardcodes), the browser blocks QUERY before your code runs.

Client retries: urllib3's default retry allow-list is {GET, HEAD, PUT, DELETE, OPTIONS, TRACE}. QUERY is idempotent—the one property that makes retry safe—but urllib3 refuses to retry it. The safety mechanism built to stop double-firing POSTs now blocks retries for the one new method that is genuinely safe to retry.

OpenTelemetry drops QUERY into _OTHER on Java and Go agents. OpenAPI 3.1 cannot name a QUERY operation. OWASP CRS blocks it with a four-token string older than the RFC.

Cache Poisoning: Live Demonstration

QUERY's cache key must include the body. RFC 10008's Security Considerations warns: "Caches that normalize QUERY content incorrectly... can return an incorrect response." I made it happen.

I configured Varnish to cache QUERY and sent three requests:

# Request 1: body {"a":1}
# Request 2: body {"a":1} (cache hit, correct)
# Request 3: body {"a":999}

Varnish served Request 1's answer for Request 3. The origin was never asked. Varnish keys on URL and host, not body. A ~40-line fix keys on a hash of the body, but production Varnish does not do this by default.

The Good News: Your Code Works

Express, Fastify, and FastAPI all route QUERY and read its body. Browsers send it via fetch and XMLHttpRequest. The runtimes are ready. The bouncers at the edge are not.

The Fix: Fallback with X-HTTP-Method-Override

Try native QUERY. If it gets a 403 or 405, retry as POST with header X-HTTP-Method-Override: QUERY. A server middleware unwraps it. The edge only sees POST, which every CDN forwards. You lose edge cacheability on the fallback path, but given Varnish's behavior, that may be wise.

Open Questions

  • Does body-blind cache poisoning reproduce on Cloudflare or Fastly with real caching? I couldn't test without your origin.
  • Anyone shipping QUERY in production? Which layer bit you first?
  • Is there a single CDN that keys cache on request body today?

Lab and Code

All scripts, raw numbers, and the safe cache implementation: http-query-lab