Every REST API I have ever worked on grows a POST /search endpoint sooner or later. Not because POST is the right method for reading data, but because GET cannot carry a request body and the filter JSON stopped fitting in the URL years ago. As of mid-June 2026, HTTP finally has a proper fix. It is called QUERY, and it just became RFC 10008.
The HTTP QUERY method gives you the thing GET and POST each refuse to: a safe, idempotent, cacheable request that carries a body. In this post I will walk through what the spec actually says, why GET-with-a-body and POST-for-reads are both broken, how caching works when the cache key lives in the request body, and which runtimes you can use it on today.
What is the HTTP QUERY method?
The HTTP QUERY method is a new request method, standardized in RFC 10008 in mid-June 2026, that sends a query as the request body while keeping the guarantees of GET. The spec's abstract puts it well: "A QUERY requests that the request target process the enclosed content in a safe and idempotent manner and then respond with the result of that processing."
The body is the query. Not the resource, not a command, a question. "The content of the request and its media type define the query." That media type matters: the spec says servers MUST fail the request if Content-Type is missing or inconsistent with the content. A successful query comes back as a plain 200 OK with the results in the response body.
Here is the comparison table straight from Section 1 of the RFC:
| Property | GET | QUERY | POST |
|---|---|---|---|
| Safe | Yes | Yes | Potentially no |
| Idempotent | Yes | Yes | No |
| Cacheable | Yes | Yes | Only for future GET or HEAD requests |
| Request body | No defined semantics | Expected | Expected |
There is also a discovery mechanism. A server can advertise QUERY support with the new Accept-Query response header, a Structured Field listing the query media types it accepts for that path:
Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"The RFC was written by Julian Reschke (greenbytes), James M. Snell (Cloudflare), and Mike Bishop (Akamai), and it is a Proposed Standard on the IETF standards track. This is not a vendor experiment. It is the real thing.
Why isn't GET with a body enough?
Sending a body with GET is undefined behavior that some servers and intermediaries will actively reject. RFC 9110, the core HTTP semantics spec, is blunt about it in Section 9.3.1: "content received in a GET request has no generally defined semantics, cannot alter the meaning or target of the request, and might lead some implementations to reject the request and close the connection because of its potential as a request smuggling attack."
So the standard advice has always been: put the query in the URL. That works until it does not. RFC 9110 only recommends that implementations support URIs of at least 8,000 octets, and that is a floor, not a promise. Real limits vary per proxy, per load balancer, per server, and you discover the smallest one in the chain at runtime. The spec for the 414 status code even names the workaround as the culprit: it happens "when a client has improperly converted a POST request to a GET request with long query information."
I have debugged exactly one 414 in my career and it cost me most of a day, because the request worked fine locally and only failed behind a corporate proxy with a tighter URI limit. That is the nature of this failure: it is environmental, and your test suite will not catch it.
RFC 10008's introduction lists two more problems with query-in-the-URL that I find underrated. First, URLs leak: "request URIs are more likely to be logged than request content and may also turn up in bookmarks." If your query contains anything sensitive, it is now in every access log along the path. Second, my favorite line in the whole spec: encoding queries into the URI "effectively casts every possible combination of query inputs as distinct resources." Your cache and your metrics now treat ?city=Berlin&limit=50 and ?limit=50&city=Berlin as different things, even though they are the same question.
Why not just use POST for queries?
POST works for queries right up until you need retries, caching, or honest semantics. The problem is not that POST fails. It is that POST tells every piece of HTTP infrastructure to assume the worst.
POST is not safe and not idempotent. When a connection drops mid-request, neither the client nor any proxy can know whether state changed on the server. So nothing retries automatically. Compare that with the QUERY abstract: QUERY requests "can be automatically repeated or restarted without concern for partial state changes." For genuinely unsafe operations you end up building retry safety by hand, which is exactly the machinery I covered in idempotency keys for REST APIs. Reads should never have needed that machinery in the first place.
And then there is caching. RFC 9110 Section 9.3.3 allows caching POST responses only when the response carries explicit freshness information plus a Content-Location that matches the target URI, and even then the cached response can only serve future GET and HEAD requests. It says directly: "a POST request cannot be satisfied by a cached POST response because POST is potentially unsafe." Two identical POST /search calls always hit your origin. Every dashboard that polls a search endpoint is doing full-price origin work on every tick.
QUERY flips all three defaults. Safe, so infrastructure can retry it. Idempotent, so retries need no bookkeeping. Cacheable, so identical questions can be answered without recomputing.
How does caching work for QUERY requests?
A QUERY response is cacheable, but the cache key must include the request content, not just the URI. That is the core trade the spec makes, and Section 2.7 is explicit: the cache key "MUST incorporate the request content" and related metadata.
This is genuinely harder than caching GET. The spec admits it: "caching QUERY method responses is inherently more complex than caching responses to GET, as complete reading of the request's content is needed in order to determine the cache key." A cache has to buffer the body before it can even decide whether it has seen this request before.
To avoid trivial misses, caches are allowed to normalize: they "MAY remove semantically insignificant differences from request content." The spec calls out stripping content encodings and using media type suffix knowledge, like treating anything ending in +json as JSON for normalization purposes. The flip side lands in the security section: normalize too aggressively and "normalization results in a false positive," meaning a cache can hand back the wrong query's results. If you build or configure a QUERY-aware cache, that is the failure mode to fear.
Conditional requests work too, through a clever indirection the spec calls the equivalent resource: a hypothetical GET-able resource derived by incorporating the request content into the target. Your ETag and If-None-Match validators operate against that, so a repeated query can come back 304 Not Modified.
Two response headers round out the model, and they are easy to confuse:
Content-Locationpoints at the query results, a URI you can plainly GET later to retrieve the same representation.Locationpoints at the query itself, a URI that re-runs the query on GET without resending the body.
One redirect trap worth knowing: the historical browser behavior of rewriting a redirected POST into a GET does not apply here. The spec states the 301/302 exceptions for POST do not apply to QUERY. Only a 303 tells the client to fetch the result with GET.
Why was it named QUERY instead of SEARCH?
The spec spent its first six years named SEARCH and dropped the name to escape WebDAV baggage. The idea goes back to a 2015 individual draft (draft-snell-search-method), got revived by Asbjorn Ulsberg at the HTTP Workshop in 2019, and was adopted by the httpbis working group in March 2021, still called SEARCH.
The rename happened in November 2021. Appendix B of the RFC explains the reasoning with unusual candor. The IANA method registry already had three safe, idempotent methods: PROPFIND, REPORT, and SEARCH. All three come out of the WebDAV effort, "about which many have mixed feelings," and all use generic XML request formats. QUERY got a clean start, and as the appendix notes, the name "captures the relation with the URI's query component well."
From there it was a slow march: final draft in November 2025, IESG approval on November 20, 2025, and publication as RFC 10008 in mid-June 2026. Eleven years from first draft to standard. HTTP does not move fast, which is exactly why it is worth paying attention when it does move.
Who supports the HTTP QUERY method in 2026?
Node.js has parsed QUERY natively since early 2024, OpenAPI 3.2 can document it, and Spring's support is an open pull request. Here is the honest state of things as of July 2026:
| Stack | Status (July 2026) |
|---|---|
| Node.js | Native. QUERY parsing landed via llhttp 9.2.0 in Node 21.7.2 and Node 22+ |
| Express | Works on QUERY-capable Node, but no TypeScript types yet |
| Fastify | Opt-in: fastify.addHttpMethod('QUERY', { hasBody: true }) |
| Go net/http | No MethodQuery constant, but arbitrary method strings work today |
| Spring Framework | Not shipped. PR #34993 is open and currently labeled blocked |
| ASP.NET Core | .NET 11 Preview 4 maps QUERY and emits it in OpenAPI output |
| OpenAPI | 3.2.0 (September 2025) added a first-class query operation field |
| curl | Generic -X QUERY with --data and an explicit Content-Type |
| Browsers (fetch) | Accepted as a method string, but always triggers a CORS preflight |
A few of these deserve context. The Spring situation is the one I am watching closest as a Java developer: the original issue (#32975) sat for two years and was superseded by PR #34993, which the author marked ready for review the same week the RFC published. As of early July 2026 the RequestMethod enum still has no QUERY, so Spring apps cannot route it through @RequestMapping yet.
On the browser side, QUERY is not a CORS-safelisted method, and the RFC says plainly that a cross-origin QUERY "will require a preflight request." Budget for the extra OPTIONS round trip, and if preflights have bitten you before, my Spring Boot CORS guide covers how to configure them properly. MDN has no QUERY page yet and there is no caniuse entry, which tells you how early we still are.
And do not assume CDN support. Two of the RFC's three authors work at Cloudflare and Akamai, which bodes well, but I could not find any announcement from either that their edge caches handle QUERY today.
How can you try the QUERY method today?
curl plus Node 22 is enough to build a working QUERY endpoint right now, no dependencies required. curl sends any method you give it:
curl -X QUERY 'http://localhost:3000/contacts' \
-H 'Content-Type: application/json' \
-d '{"filter": {"city": "Berlin"}, "limit": 50}'On the server side, Node's HTTP parser accepts QUERY natively, so req.method just works:
import { createServer } from 'node:http';
const server = createServer((req, res) => {
if (req.method !== 'QUERY') {
res.writeHead(405, { Allow: 'QUERY' });
res.end();
return;
}
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', () => {
const query = JSON.parse(body);
// Run the actual search here. The body IS the query.
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ query, results: [] }));
});
});
server.listen(3000);From a browser or any JavaScript runtime, fetch takes QUERY as a plain method string:
const res = await fetch('http://localhost:3000/contacts', {
method: 'QUERY',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filter: { city: 'Berlin' }, limit: 50 }),
});
const { results } = await res.json();Remember the preflight: cross-origin, this fires an OPTIONS request first, every time, because QUERY is not safelisted.
If you are on Spring, you are stuck waiting or working around. Since RequestMethod does not know QUERY, requests with the method never reach your controllers through normal mapping. A servlet filter registered ahead of Spring MVC can intercept "QUERY".equals(request.getMethod()) and dispatch manually, but I would treat that as a stopgap and keep an eye on PR #34993 instead.
Should you adopt QUERY now?
For internal services on Node, I would start using QUERY today. The parser support is there, the semantics are standardized, and internal traffic does not care about CDN or browser maturity. Every retry policy and cache layer you control can start taking advantage of safe-and-idempotent reads immediately.
For public APIs, advertise before you commit. Ship Accept-Query on endpoints that support it, keep the POST variant alive for older clients, and let OpenAPI 3.2 document both. The tooling wave is coming from an interesting direction: with OpenAPI 3.2 and ASP.NET 11 both recognizing QUERY, generated clients will start offering it before most hand-written servers do.
The 420-point Hacker News thread that greeted the RFC in June 2026 suggests developers have been waiting for this one. I think POST /search will read as a legacy idiom within a few years, the same way X- headers do now. The method finally matches the meaning.
For the primary sources, read RFC 10008 itself, the HTTP semantics it builds on in RFC 9110, and the OpenAPI 3.2.0 announcement that added first-class QUERY support.
Keep Reading
- How to Implement Idempotency Keys in REST APIs the Right Way. QUERY gives reads the retry safety that writes still have to build by hand.
- Solving Spring Boot CORS Errors Once and For All. Every cross-origin QUERY triggers a preflight, so get your CORS config right first.
- How to Version REST APIs in Spring Framework 7 (Spring Boot 4). More API design decisions in the current Spring generation.