HTTP QUERY Method Explained: The REST Fix for GET vs POST (RFC 10008)
The HTTP QUERY method (RFC 10008) is a safe, idempotent, cacheable request with a body — the REST method that ends the GET-vs-POST workaround. Full guide.

The HTTP QUERY method (RFC 10008, June 2026) is a new REST request method that is safe and idempotent like GET but carries the query in a request body like POST — so large, structured queries finally get HTTP-layer caching and automatic retries. It’s the first general-purpose method HTTP has gained since PATCH in 2010, and it closes a design gap REST API developers have been working around for 25 years. If you’ve ever tunneled a read-only search through POST and silently given up caching and retry safety, this method exists because of you.
THE HTTP QUERY METHOD AT A GLANCE
RFC 10008
Proposed Standard
published June 2026
Safe
+ idempotent
retryable by definition
URL + body
cache key
shared caches can store results
16 yrs
since the last new method
PATCH, RFC 5789 (2010)
TL;DR
- QUERY is a new HTTP request method that sends a query in the request body while guaranteeing the operation is safe and idempotent — no state change, freely retryable, and cacheable by proxies and CDNs.
- It exists because GET can’t reliably carry a body (the spec gives GET bodies “no defined semantics”, and URLs cap out around 2,000–8,000 characters) while POST forfeits caching and automatic retries even for purely read-only queries.
- The cache key for a QUERY request is the URL plus the request body, so two identical searches can be served from the edge without touching your origin — something POST-based search endpoints will never get.
- It’s already real: Node.js parses QUERY natively (21.7.2+/22+), Fastify supports it via
addHttpMethod(), nginx landed basic support, ASP.NET Core recognizes it in .NET 11 previews, and OpenAPI 3.2 models it — but browsers’fetch()can’t send it yet. - My take: design new server-to-server search/filter/report endpoints as QUERY-shaped now (single query document in the body, mandatory
Content-Type), even if you expose them via POST today. The migration later becomes a method-name change.
What Is the HTTP QUERY Method?
The HTTP QUERY method is a safe, idempotent request method, defined in RFC 10008, that asks a resource to run a query described by the request body and return the result. The body — with a mandatory Content-Type — carries the query parameters, so the URL no longer has to. Unlike POST, the server promises the operation changes nothing; unlike GET, the query can be as large and structured as you want: JSON documents, GraphQL queries, SQL-ish filters, JSONPath expressions.
The RFC’s own framing is precise: a QUERY request asks “the target resource to perform a query operation within the scope of that target resource.” Two guarantees make everything else possible. First, safety: “the client does not request or expect any change to the state of the target resource.” Second, idempotency: QUERY requests “can be retried or repeated when needed, for instance, after a connection failure.”
That second sentence is the quiet superpower. A dropped connection mid-POST leaves your client guessing — did the server process it? A dropped connection mid-QUERY is a non-event: any client, library, or proxy may resend it without asking.
Why HTTP Needed a New Method
Here’s the design flaw we all normalized: HTTP made you choose between honest semantics and a request body.
GET is the semantically correct method for reads. Every cache, proxy, prefetcher, and crawler on Earth understands it. But RFC 9110 gives content in a GET request “no generally defined semantics” — intermediaries may drop it, some servers reject it, and browsers won’t send it. So real query data had to squeeze into the URL, which breaks somewhere between 2,000 and 8,000 characters depending on the browser, proxy, and server in the chain.
POST takes any body you like. But POST is defined as potentially unsafe and non-idempotent, so every intermediary treats your read-only search like a payment submission: no shared caching, no automatic retry, no prefetching. Every GraphQL query — explicitly read-only by design — loses HTTP-level caching and retry safety the moment it ships as POST.
The industry’s workarounds tell the story better than any spec rationale:
THE WORKAROUND ERA VS QUERY
What read-heavy APIs did for two decades, and what RFC 10008 replaces it with.
BEFORE — THE HACKS
Tunneling reads through the wrong method
- Elasticsearch ships GET-with-body for _search — technically undefined behavior that some proxies silently drop
- GraphQL standardizes on POST /graphql for every query — zero HTTP caching, custom retry logic in every client
- REST APIs grow POST /orders/search endpoints that look like writes to every tool in the chain
- Teams base64-encode JSON filters into query strings until the URL length limit bites
AFTER — RFC 10008
One method that says what it means
- QUERY /orders carries the full filter document in the body with a mandatory Content-Type
- Safety and idempotency are guaranteed by the method itself — proxies can cache, clients can retry
- The cache key incorporates the body, so identical queries hit the edge cache
- Accept-Query lets servers advertise exactly which query formats they accept
The workarounds all worked. They also all lied to the infrastructure — and the infrastructure priced that lie in lost caching and manual retry code.
This isn’t a new idea, which makes the 18-year journey instructive. WebDAV had a body-carrying SEARCH method back in RFC 5323 (2008), but it never escaped WebDAV’s orbit. The IETF HTTP working group adopted draft-ietf-httpbis-safe-method-w-body in 2021, initially to generalize SEARCH, renamed it QUERY by draft-02 in 2022 to shed the WebDAV baggage, and shipped RFC 10008 after fourteen draft revisions. The author list explains the industry buy-in: Julian Reschke (greenbytes, co-editor of the core HTTP specs), James Snell (Cloudflare), and Mike Bishop (Akamai). When the two largest CDNs co-author a caching-focused method, they intend to implement it.
How QUERY Works on the Wire
A QUERY request looks like a POST that tells the truth. Here’s the RFC’s canonical example — a form-encoded query against a contacts collection:
A MINIMAL QUERY EXCHANGE
The body carries the query; Content-Type is mandatory; the response is an ordinary, cacheable 200.
QUERY /contacts HTTP/1.1
Host: api.example.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
select=surname,givenname,email&limit=10
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=60
[ ...ten contacts... ]
Swap the Content-Type for application/json, application/graphql, or your own media type — the method doesn't care, the resource defines what queries mean.
Beyond the basic exchange, RFC 10008 nails down four behaviors that make QUERY more than “POST, but pinky-promise it’s safe”:
1. Content-Type is not optional. Servers “MUST fail the request if the Content-Type request field is missing or is inconsistent with the request content.” No content sniffing, no guessing — a sharp break from POST’s anything-goes reality.
2. Discovery via Accept-Query. A server advertises QUERY support and its accepted formats with a response header using Structured Field syntax:
OPTIONS /contacts HTTP/1.1
Host: api.example.com
HTTP/1.1 204 No Content
Allow: GET, HEAD, OPTIONS, QUERY
Accept-Query: application/x-www-form-urlencoded, application/jsonpath One subtlety worth knowing: the Accept-Query value “applies to every URI on the server that shares the same path” — the URL’s query component is ignored for this purpose.
3. Results can get their own URIs. A 2xx response may include a Location header naming a URI you can later GET to re-run the equivalent query, or a Content-Location naming a resource that holds this specific result snapshot. That turns an expensive ad-hoc query into a shareable, bookmarkable, cacheable resource — a genuinely RESTful touch POST never standardized for reads.
4. Redirects behave like you’d hope. A 301 or 308 means repeat the QUERY at the new target; 303 means fetch the result with a plain GET. Crucially, the legacy exception that lets clients degrade a redirected POST into GET “does not apply to QUERY requests” — your query body survives the redirect.
Caching: The Actual Killer Feature
The RFC is blunt about the mechanics: “The cache key for a QUERY request MUST incorporate the request content and related metadata.” URL plus body, hashed, stored. Identical query, identical key, edge-served response.
Caches are even allowed to normalize semantically insignificant differences — stripping content encoding, or applying format-aware knowledge (JSON key order, whitespace) — so trivially different bodies can still share a cache entry. If that makes you nervous, no-transform opts out.
Think about what this does for a search-heavy API. Today, your “cache” for POST /search is an application-level Redis layer you built, keyed by a hash you invented, invalidated by rules you maintain. QUERY moves that entire concern into infrastructure that already exists at every hop between the user and your origin. This is the same architectural instinct as moving analytics to the edge with Cloudflare Workers — push work to the layer that’s already positioned to do it.
💡 Key insight: QUERY doesn’t make anything possible that was impossible before. It makes the default correct — caching, retries, and semantics you previously had to hand-build now fall out of the method name.
QUERY vs GET vs POST
The one-table version, straight from the RFC’s own comparison:
| Property | GET | QUERY | POST |
|---|---|---|---|
| Safe (no state change) | Yes | Yes — by definition | No guarantee |
| Idempotent (retryable) | Yes | Yes — by definition | No guarantee |
| Request body | No defined semantics | Yes — carries the query | Yes |
| Shared-cache friendly | Yes (URL key) | Yes (URL + body key) | Effectively no |
| Query size limit | ~2K–8K chars in URL | Body-sized | Body-sized |
| Sensitive params in logs | URL is logged everywhere | Body rarely logged | Body rarely logged |
| CORS without preflight | Yes | No — always preflights | Sometimes |
That security row deserves a sentence: because the query moves out of the URL, it stops leaking into access logs, browser history, referrer chains, and analytics pipelines. RFC 10008’s Security Considerations calls this out as a real benefit for sensitive queries — with the matching warning that servers minting result URIs must not embed sensitive query parts back into them.
Where QUERY Works Today (July 2026)
The honest adoption picture, one month after publication:
On the working side: Node.js parses QUERY natively since 21.7.2 and 22+, and Fastify exposes it with one line. nginx landed basic upstream support, Spring Framework has an active PR (#34993), Rails has a live core proposal, and ASP.NET Core recognizes it in .NET 11 previews.
OpenAPI 3.2 also models QUERY operations — which means generated clients will offer it before most hand-written servers accept it.
Here’s a working Fastify endpoint today:
import Fastify from 'fastify';
const app = Fastify();
app.addHttpMethod('QUERY', { hasBody: true });
app.route({
method: 'QUERY',
url: '/products',
handler: async (req, reply) => {
const results = await search(req.body); // body IS the query
reply.header('cache-control', 'public, max-age=60');
return results;
}
}); And from any client that lets you set a custom method:
curl -X QUERY https://api.example.com/products
-H 'content-type: application/json'
-d '{ "category": "keyboards", "maxPrice": 200, "sort": "rating" }' The missing piece is the browser. fetch() can’t send QUERY until the WHATWG Fetch spec adds it, and even then QUERY is not a CORS-safelisted method, so cross-origin requests will always preflight. In 2026, QUERY is a server-to-server and API-gateway story — which is exactly where search, reporting, and internal query traffic lives anyway. If you build backend-for-frontend layers in Node.js, that BFF-to-service hop is the perfect first deployment.
Common Mistakes to Avoid
Four more, seen in the wild already:
- Omitting
Content-Type. Legal-ish on POST, a mandatory failure on QUERY. Fail these requests loudly, as the RFC requires — don’t sniff. - Forgetting the CORS preflight. QUERY from browser-adjacent contexts will send OPTIONS first. Your gateway needs
Access-Control-Allow-Methods: QUERYor every request dies preflight. - Leaking query content into result URIs. If QUERY returns a
Locationfor later GETs, that URL will be logged everywhere. Use opaque result IDs, not serialized query parameters. - Assuming your WAF and proxies pass it through. Older intermediaries reject unknown methods. Test the full path — one method-allowlist in a corporate proxy can 405 your rollout.
Should You Adopt QUERY Now?
THE ADOPTION CALL
For teams designing or evolving REST APIs in 2026.
The pros
- Correct semantics for read-only queries with real bodies — no more lying to infrastructure
- HTTP-layer caching for search endpoints, with the body in the cache key
- Free retry safety on flaky networks — no custom retry-with-dedupe logic
- Sensitive query params move out of URLs, logs, and referrer chains
- Backed by Cloudflare and Akamai authorship plus OpenAPI 3.2 — the tooling wave is coming
The cons
- No browser fetch() support yet — server-to-server only for now
- Framework support is uneven: Fastify yes; Express, Django, FastAPI not yet
- Always triggers CORS preflight — never a "simple request"
- Old proxies and WAFs may 405 an unknown method
- Cache normalization is subtle — a misconfigured cache can serve wrong results
Adopt the shape now, the verb where your stack allows. Design query endpoints as single-body query documents behind POST today, and flipping to QUERY later is a rename, not a rewrite.
A PRAGMATIC MIGRATION PATH
How I'd move a search-heavy API to QUERY without betting the roadmap on it.
STEP 1
Make endpoints QUERY-shaped behind POST
One query document in the body, mandatory Content-Type, handler verifiably side-effect free. This costs nothing and pays off regardless.
STEP 2
Dual-route QUERY and POST to the same handler
Where your stack supports it (Node/Fastify, nginx, .NET 11 previews), accept both verbs. Advertise support via Allow and Accept-Query on OPTIONS.
STEP 3
Turn on HTTP caching for the QUERY route
Add Cache-Control, watch your origin query load drop for repeated searches, and validate cache-key behavior with your CDN as edge support lands.
STEP 4
Deprecate the POST tunnel when clients catch up
OpenAPI 3.2 already models QUERY, so generated clients arrive first. Keep POST as a compatibility alias as long as you need.
Why this step existsSame handler, honest semantics, and caching you didn’t have to build.
Before you ship a QUERY endpoint
Track progress as you work through the list
0%
0/6 done
FAQ
Questions readers usually have
Common questions about the HTTP QUERY method and RFC 10008.
The Bottom Line
REST API design has spent 25 years contorting reads into methods that either couldn’t carry the query or couldn’t admit it was a read. The HTTP QUERY method ends that trade-off with the least glamorous, most durable kind of fix: a verb that tells the infrastructure the truth, and infrastructure that rewards honesty with caching and retries you no longer have to build.
You don’t need to rewrite anything this quarter. But every new search, filter, or reporting endpoint you design from today should be QUERY-shaped: one query document in the body, a mandatory content type, and a handler with zero side effects. Do that, and adopting the verb itself — when your framework, gateway, and CDN all catch up — becomes a one-line change instead of a migration project.
If you found this useful, read Node.js Backend for Frontend Developers next — the BFF layer is exactly where QUERY will land first — or see how FastAPI’s app.frontend() rethinks another old default.
Sources
- RFC 10008 — The HTTP QUERY Method (IETF, June 2026)
- draft-ietf-httpbis-safe-method-w-body — draft history (IETF Datatracker)
- RFC 9110 — HTTP Semantics (GET body semantics, method properties)
- RFC 5323 — WebDAV SEARCH (the 2008 precursor)
- Spring Framework PR #34993 — Add RFC 10008 (QUERY) support
- Ruby on Rails core proposal — Support for the HTTP QUERY method
- RFC 10008: HTTP QUERY Method Ends the POST Workaround (byteiota, adoption roundup)
Written for umesh-malik.com — no-fluff technical writing on AI, Web Dev, and Engineering.
About the Author
Software engineer writing about AI, Claude Code, LLMs, OpenAI, Anthropic, and developer tooling. 5+ years building production systems at Expedia Group, Tekion, and BYJU'S.
Related Articles

Web Engineering
FastAPI Finally Has Native SPA Support: app.frontend() Explained
FastAPI 0.138.0 ships app.frontend() — a native way to serve React, Vue, and Svelte SPA builds. How it works, real use cases, and what it still can't do.

Web Engineering
The $1,100 Framework That Just Made Vercel's $3 Billion Moat Obsolete
One engineer + Claude AI rebuilt Next.js in 7 days for $1,100. The result: 4.4x faster builds, 57% smaller bundles, already powering CIO.gov in production. This is the moment AI-built infrastructure became real—and everything about software development just changed.

Web Engineering
Node.js Just Cut Its Memory in Half — One Docker Line, Zero Code Changes, $300K Saved
V8 pointer compression finally lands in Node.js: one Docker image swap cuts heap memory ~50%, improves P99 latency, and can save $80K–$300K a year.