A Method for the Madness: Meet HTTP QUERY

Note to the reader: I saw a headline on the new QUERY method and in a quick read didn’t really understand the need, so I did a deep dive with Claude to understand the method itself, then followed with the larger infrastructure, security, and operational impacts for the implementation transitional period. This article is a summary of that session and co-authored by Claude.

Every REST API grows a POST /search endpoint sooner or later — not because POST is the right tool for reading data, but because GET can’t carry a body and your filter JSON stopped fitting in the URL years ago. The IETF closed that gap. On 15 June 2026 the RFC Editor published RFC 10008, “The HTTP QUERY Method” — a 24-page Proposed Standard on the IETF Standards Track. It’s the first genuinely new HTTP method since PATCH landed as RFC 5789 in 2010, a 16-year gap. QUERY gives you the one thing GET and POST each refuse to provide: a safe, idempotent, cacheable request that carries a body.

The TL;DR

  • What it is: QUERY is a new, IANA-registered HTTP method that is safe and idempotent like GET, but carries a request body like POST. The body defines the query.
  • Why it exists: GET is semantically correct for reads but forces the query into the URL (length limits, encoding pain, logging/bookmarking leakage). POST /search carries a body but lies to the infrastructure — nothing in the protocol signals the request is read-only, so caches, retry logic, and proxies can’t treat it as one. QUERY is the missing middle.
  • What’s new on the wire: Nothing special. Any existing HTTP/1.1 or HTTP/2 connection carries it today. The friction is at every layer that has opinions about methods.
  • The security headline: “Safe” is a promise about intent, not a guarantee about payloads. QUERY bodies need POST-level inspection, cache keys must include the full body (or you get cache poisoning), CSRF checks still apply to any endpoint with side effects, and method allowlists written before June 2026 don’t know QUERY exists.
  • Where support stands (mid-2026): Node.js has parsed QUERY since early 2024; OpenAPI 3.2 documents it; browsers can send it via fetch()/XHR but don’t yet cache it; declarative HTML forms fall back to GET and drop the body; Spring hasn’t shipped support. Treat it as production-ready for server-to-server, early-stage for browser-facing traffic.
  • What to do: Don’t rip out POST /search. QUERY sits alongside GET and POST — migrate gradually, and confirm every infrastructure layer handles it deliberately before real traffic arrives.

The two-tool problem

For as long as most of us have built APIs, reading data from a server meant choosing between two imperfect tools.

GET has exactly the semantics you want for reads: safe (no state change expected), idempotent, and cacheable. The catch is that GET has no generally defined semantics for a request body — core HTTP semantics (RFC 9110 §9.3.1) even warn that a body on a GET can lead implementations to reject the request or treat it as a request-smuggling attempt. So the query has to go in the URL, and that runs into hard walls: URL length limits vary per proxy, load balancer, and server, and you discover the smallest one in the chain at runtime. RFC 9110 only recommends supporting URIs of at least ~8,000 octets, and that’s a floor, not a promise. Some data is awkward to encode in a URI at all. Request URIs get logged, bookmarked, and dropped into Referer headers and analytics. And a nested filter object with arrays and boolean logic becomes an unreadable, fragile query string.

POST fixes the body problem and creates a semantic one. A POST can create a record, update state, trigger a job, submit a form, or just run a search — and the HTTP layer can’t tell which. Your application knows POST /search is read-only, but that’s a private agreement between the server and the humans who wrote it. The caches, proxies, retry logic, CORS, gateways, and WAFs in the middle only see “POST,” so they treat it as unsafe: not cacheable, not auto-retryable. Even a 200 OK doesn’t tell them whether state changed.

What QUERY actually does

QUERY is the obvious hybrid: body support from POST, safe-and-idempotent semantics from GET.

QUERY /users HTTP/1.1 Host: example.org Content-Type: application/json { “role”: “admin”, “status”: “active”, “sort”: “name”, “page”: 1 }

Because the method is declared safe and idempotent — and IANA-registered as such — the infrastructure can finally reason about it. A dropped QUERY can be automatically retried or restarted without worrying about partial state changes. Responses are cacheable. And the request finally tells the truth about what it’s doing.

A few spec details worth knowing:

  • No new status codes, no new content types. QUERY reuses the existing vocabulary. A missing media type earns a 400; an unsupported one earns a 415; a syntactically valid query that can’t be processed (say, correct SQL against a nonexistent table) earns a 422.
  • Accept-Query for discovery. A response header a resource uses to advertise QUERY support and the media types it accepts, so clients can discover support instead of trial-and-erroring into a 405 Method Not Allowed.
  • Cache key includes the body. This is the whole trick that makes a body-carrying request cacheable — and, as we’ll see, the whole security footgun.
  • A URI escape hatch. The server can respond to a QUERY with a Location / Content-Location header, assigning a stable URI to the query and its result, so clients can GET that URI afterward. This restores bookmarking and sharing, which you lose when the condition moves from the URL into the body.

The name, for the curious: early drafts used SEARCH, borrowed from the WebDAV family (alongside PROPFIND and REPORT). The working group settled on QUERY because it maps cleanly to the URI’s query component and describes a generic safe-read operation rather than implying one specific use case.

The security implications most coverage skips

Here’s the part that matters more than the protocol itself. QUERY is well specified; the surrounding security assumptions are what need a second look. For nearly three decades, developers and security teams have built applications, APIs, WAFs, proxies, and tooling around a fixed set of verbs — GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS. A new one that behaves like a GET/POST hybrid breaks assumptions baked into all of those layers.

1. “Safe” describes intent, not payload

This is the single most important thing to internalize. The safe designation means the client is not requesting a state change. It says nothing about whether the body is malicious. A QUERY body can carry SQL injection, XSS, command injection, or an oversized payload just as easily as a POST body can. Any WAF/IDS rule set that inspects POST bodies for these patterns must apply the same coverage to QUERY — SQLi, XSS, and command-injection inspection, plus request size limits. Give QUERY POST-level scrutiny at the body, even while trusting its read-only semantics for caching and retries. Treating “safe” as “harmless” is the mistake that turns a clean protocol upgrade into an open door.

2. Cache poisoning and cache deception

QUERY is explicitly cacheable, and RFC 10008’s cache model requires the cache key to incorporate the request body. That’s non-negotiable, because otherwise two different queries to the same URL would collide. The risk: a cache that hashes or normalizes the body incorrectly — or normalizes it differently than the origin processes it — can return the wrong response on a false-positive key match. That’s cache poisoning (an attacker plants a response that later gets served to victims) and cache deception (a victim’s sensitive response gets cached under a key an attacker can retrieve) in one bug class. It’s the same underlying failure as the GitHub Actions cache-poisoning attacks — the cache key doesn’t capture the full request — just at a different layer of the stack. Any cache you enable for QUERY needs an explicit audit of how it derives keys from the body.

Worth noting: as of mid-2026, current Chrome and Firefox send QUERY but don’t cache repeated identical QUERYs yet. So browser-side caching is unimplemented rather than solved — the poisoning surface today lives in your CDN, reverse proxy, and API gateway.

3. The CSRF trap

Because QUERY is labeled “safe,” it’s tempting to exclude it from CSRF protection the way you might exclude GET. Don’t. The safe designation is a semantic promise, not an enforcement mechanism. If any endpoint accepts QUERY and has any side effect — logging that mutates state, a search that also writes an audit record, a “read” that lazily provisions something — it must remain covered by CSRF checks regardless of the method’s designation. Middleware that method-matches on POST|PUT|DELETE|PATCH will silently skip QUERY. Extend it explicitly.

4. Method allowlists and request-smuggling flags

Most WAFs, API gateways, and load balancers enforce method allowlists written as literal sets — and those written before June 2026 don’t mention QUERY. Two failure modes result, and they pull in opposite directions:

  • Reject: strict setups drop QUERY as an unknown verb, and your rollout silently fails at the edge.
  • Pass through uninspected: looser setups forward QUERY but skip the body inspection they apply to POST — the worst outcome, an inspection blind spot.

There’s also a genuine request-smuggling dimension. Many enterprise WAFs flag non-standard verbs as protocol anomalies or smuggling vectors, precisely because inconsistent method handling across a proxy chain is how desync attacks start. If your CDN, WAF, and origin disagree about whether QUERY is valid — or one preserves the body while another strips it — you’ve created exactly the kind of parsing inconsistency smuggling exploits. On AWS, for example, CloudFront must be explicitly configured to forward QUERY with its body, and AWS WAF Core Rule Sets need QUERY deliberately whitelisted rather than caught by anomaly rules. The fix everywhere is the same: make QUERY a deliberate decision at every hop, not an accident.

For penetration testers, the flip side is a fresh request type worth throwing at filters tuned for the usual suspects. Rules that catch a POST-body payload may not fire on the identical payload sent via QUERY.

5. Content-Type must be enforced, not sniffed

RFC 10008 requires rejecting requests whose Content-Type is missing or inconsistent with the body, and explicitly forbids content sniffing — a server may not guess the media type and override a missing or wrong one. If you accept application/json, parse it as JSON; if you use a custom query DSL, define its media type and advertise it via Accept-Query. Leave this vague and you’ve simply carried the implicit, undocumented conventions of the POST /search era onto a shiny new method — with the added risk that a lenient parser and a strict one in your chain disagree about what the body means.

6. The logging trade-off is real but not free

Moving search conditions out of the URL and into the body is a genuine privacy win: URIs persist in access logs, analytics, browser history, Referer headers, and bookmarks, and sensitive selectors don’t belong there. But if your API gateway or application logs request bodies — many do, for debugging — those conditions end up recorded anyway, now in a place your log-redaction rules may not cover. You haven’t eliminated the leak; you’ve relocated it. Audit body logging and redaction on QUERY endpoints as deliberately as you’d audit URL logging.

7. CORS preflight is mandatory

QUERY is not on the CORS-safelisted method list, so browser JavaScript must send an OPTIONS preflight before any cross-origin QUERY. Test how your actual edge and application stack handles that preflight flow — a misconfigured CORS policy that reflexively allows a new method, or one that blocks the preflight, will either over-permit or silently break browser clients.

8. Observability blind spots

Dashboards, log parsers, rate-limit buckets, and SIEM detection rules keyed on method names will bucket QUERY as “unknown” or drop it entirely. During the transition, flag QUERY traffic for visibility — a Sigma-style rule that surfaces QUERY requests so you can confirm they’re expected — and tune it to your known-good clients as adoption grows. The goal is deliberate awareness during the transition, not a permanent alert.

Where support actually stands

A method this fundamental gets adopted layer by layer, and that’s normal. The honest picture as of early July 2026:

  • Node.js — parses QUERY at the HTTP layer, and has since early 2024, ahead of standardization.
  • OpenAPI 3.2 — has a place to document QUERY operations.
  • Browsers — fetch(url, { method: ‘QUERY’, body }) and XHR work (QUERY is neither forbidden nor normalized away), but neither Chrome nor Firefox caches QUERY responses yet, and declarative <form method=“query”> falls back to GET and drops the body. Mozilla and WebKit standards positions are in progress.
  • Spring — close, but hasn’t shipped support.
  • CDNs — Cloudflare and Akamai co-authored the RFC, so edge support may well arrive before framework integrations. Combined with the cache-key caveat, that means your edge may understand QUERY before your app does — plan accordingly.

The practical read: solid for server-to-server APIs and backend services today; early-stage for public browser-facing traffic.

A general adoption checklist

QUERY is a semantically clean upgrade path, not a replacement. Existing POST /search endpoints keep working; migrate gradually. Before QUERY traffic shows up against your applications, confirm each layer treats it deliberately:

  1. Edge / CDN — Confirm your CDN is configured to recognize QUERY and forward it to the origin with its body intact, not discard the payload.
  2. WAF / API gateway — Add QUERY to method allowlists explicitly. Apply POST-level body inspection (SQLi, XSS, command injection) and size limits. Make sure anomaly/smuggling rules don’t flag it by accident, and that no hop strips the body.
  3. Cache layer — Verify cache keys are built from the full request body, not just the URL and headers. Audit body normalization for false-positive collisions before enabling QUERY caching.
  4. CSRF middleware — Extend coverage to QUERY on any endpoint with side effects. Don’t let a “safe” designation exclude it from protection.
  5. Content-Type handling — Enforce a required, consistent Content-Type; reject missing or mismatched types; never content-sniff. Advertise supported types via Accept-Query.
  6. Logging & redaction — Confirm request-body logging on QUERY endpoints is covered by the same redaction rules you’d apply to sensitive URL parameters.
  7. CORS — If browser clients are in scope, test the OPTIONS preflight flow end to end. Confirm your policy neither over-permits nor blocks the new method.
  8. Client tooling — Confirm the SDKs and HTTP libraries your consumers use support QUERY, and plan a POST-fallback path (or the Accept-Query discovery flow) for those that don’t.
  9. Observability — Update method-keyed dashboards, rate limiters, and SIEM rules to recognize QUERY. Add a detection rule to surface QUERY traffic during rollout, tuned to known-good clients.

Get those nine right and QUERY is exactly what the web has been missing for 16 years: a request that finally tells the truth about what it’s doing. Skip them, and you’ve handed every layer of your stack a verb it doesn’t understand.

2 Likes

@JRahm thank you for the article.
Do you know whether the QUERY method will be supported by default in an upcoming ASM release, alongside GET, HEAD, and POST, without requiring manual policy modifications?

I haven’t encountered any QUERY requests in my environments yet, but many existing GET requests could be good candidates for this new method. As client-side support becomes more widespread, applications may start using QUERY more broadly, potentially creating issues if ASM doesn’t support it out of the box. It would therefore be helpful to have support in place before adoption increases.
Without mentioning the OpenAPI 3.2.x support (3.2.0 is now 1y old)…