If you have ever written this, this article is for you:
// a "search" that is actually a POST
axios.post('/api/search', { category: 'shoes', maxPrice: 100 })
Or the vanilla version:
fetch('/api/search', {
method: 'POST',
body: JSON.stringify({ category: 'shoes', maxPrice: 100 })
})
We all did it. The query was too big for a URL, so we used POST. It worked. But it was always a lie. We were telling HTTP "this modifies something" when it didn't.
In June 2026 the IETF published RFC 10008. It defines a new HTTP method called QUERY. It is the first new method in more than two decades. And it fixes exactly this.
The problem in 30 seconds
GET is perfect for reading data. It is safe, idempotent and cacheable. But it cannot carry a body, and URLs have length limits. Around 8000 characters if you are lucky. Less if a proxy in the middle disagrees.
POST carries any body you want. But HTTP assumes it changes state. So no automatic caching. No safe retries. If a POST times out, the client cannot just retry it. Maybe it already did something.
For years we had to pick one. Clean semantics or a real payload. Never both.
What QUERY does
QUERY is a GET with a body. That's it.
QUERY /products HTTP/1.1
Content-Type: application/json
{
"category": "shoes",
"maxPrice": 100,
"sort": "price_asc"
}
I use axios for most of my projects, so this is how it looks there:
const { data } = await axios({
method: 'QUERY',
url: '/api/products',
data: {
category: 'shoes',
maxPrice: 100,
sort: 'price_asc'
}
})
There is no axios.query() helper yet, but axios passes any custom method straight through to the underlying HTTP client, in the browser and in Node. No plugin needed.
If you prefer plain fetch, same idea:
const res = await fetch('/api/products', {
method: 'QUERY',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
category: 'shoes',
maxPrice: 100,
sort: 'price_asc'
})
})
const data = await res.json()
The server runs the query and returns the result. Nothing changes on the server. The spec guarantees two things:
- Safe. It does not modify state.
- Idempotent. Sending it twice gives the same result as sending it once.
Those two guarantees are what everything below is built on.
This pattern is going to change
Look at almost any API in production today:
POST /search <- reads data, says it writes
POST /reports/run <- reads data, says it writes
POST /products <- actually writes
Two of those three endpoints are lying. Here is where APIs are heading:
QUERY /search <- reads
QUERY /reports <- reads
POST /products <- writes
Not overnight. But the direction is clear. So the two questions that matter are: what works today, and how will our apps actually change.
Compatibility: where we are right now
The RFC is weeks old, so let's be honest about the current state.
The protocol side is already fine. HTTP does not have a fixed list of methods. QUERY is just a token in the request line. Any HTTP/1.1 or HTTP/2 connection can carry it today.
Servers. Nginx and Apache will pass QUERY requests through, but your config may need small tweaks. Anything that whitelists methods (limit_except, security rules, WAFs) needs QUERY added explicitly. This is where most early friction will live.
Frameworks. This is the real bottleneck. Your router needs to understand the method. Spring has a pull request in progress. Rails is discussing it. Express lets you handle custom methods with a bit of manual work. Expect native support to roll out across 2026 and 2027.
HTTP clients. Axios and fetch can already send arbitrary methods, so both of these work today:
// axios
axios.request({
method: 'QUERY',
url: '/api/products',
data: { category: 'shoes', maxPrice: 100 }
})
// vanilla fetch
fetch('/api/products', {
method: 'QUERY',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ category: 'shoes', maxPrice: 100 })
})
One detail worth knowing: axios ignores the body only on GET requests in browsers, because fetch and XHR do not allow it. For any other method, including QUERY, data becomes the request body without touching transformRequest.
The catch in browsers is CORS. QUERY is not a safelisted method like GET or POST, so cross-origin requests trigger a preflight. This applies to axios and fetch equally, since underneath it is the same mechanism. Your server has to answer the OPTIONS request with:
Access-Control-Allow-Methods: GET, POST, QUERY, OPTIONS
Same-origin requests need nothing. And in Node there is no CORS at all, so server-to-server QUERY calls work right now.
CDNs and proxies. Here is the interesting signal: the RFC was co-authored by engineers from Cloudflare and Akamai. CDN support may land before your framework ships it. That is not a coincidence. Caching is where QUERY pays off the most.
Discovery. The RFC also defines a response header, Accept-Query. A server uses it to announce that an endpoint supports QUERY and which formats it accepts. So clients and tools can detect support instead of guessing.
How our apps will change
This is the part I find more interesting than the spec itself. The migration is not just renaming a method. Some habits and workarounds simply stop making sense.
1. The fake POST endpoints disappear
Every /search, /filter, /reports endpoint that uses POST today is a QUERY endpoint waiting to happen. The body stays identical. Only the method changes:
// before
axios.post('/api/search', filters)
// after
axios.request({ method: 'QUERY', url: '/api/search', data: filters })
This will be the first and easiest migration in most codebases.
2. Retry logic gets simpler
Today, if a POST search times out, most clients do not retry. They can't. Maybe the server already processed it. So we write error screens and "try again" buttons.
With QUERY, clients and SDKs can retry automatically. The request is idempotent by contract. A whole category of defensive code in mobile apps and flaky-network handling gets thinner.
3. Caching layers move down the stack
Right now, caching a POST search means building it yourself. Redis, cache keys hashed from the body, invalidation logic. Application code.
With QUERY, the CDN can do it. Same body, same cached response, standard HTTP semantics. A lot of hand-rolled cache layers will quietly get deleted.
4. GraphQL gets a proper home
Every GraphQL query today travels over POST, even though almost all of them are pure reads. That is the most visible version of this problem. QUERY gives GraphQL correct semantics without touching the query language. Expect GraphQL clients and servers to adopt it early.
5. URLs get readable again
We have all seen this:
GET /products?filters=%7B%22category%22%3A%22shoes%22%7D&sort=price
JSON, URL-encoded, jammed into a query param because the framework offered nothing better. Those endpoints migrate to QUERY and the URL goes back to being just /products. Cleaner logs too. Query bodies do not end up in access logs and bookmarks the way URLs do.
6. Reading an API becomes self-documenting
When the method itself tells you if an endpoint reads or writes, documentation gets lighter and integration mistakes go down. New devs on your team stop asking "does this POST actually change anything?"
What I would do now
Nothing dramatic. But you can prepare:
- Audit your API. List every POST endpoint that only reads. Those are your future QUERY endpoints.
- Check your infra. WAFs, proxies and method whitelists that would block an unknown method.
- Watch your framework's releases through 2026 and 2027.
- If you control both client and server, nothing stops you from experimenting now with a small middleware.
Twenty years for a new HTTP method. The migration will take a while. The direction is already clear.
Top comments (2)
"We were telling HTTP this modifies something when it didn't" is the whole thing, and the people who paid the most for that lie are not building CRUD, they are building search and retrieval. A RAG or vector query is a read: filters, a top-k, sometimes the query embedding itself, a payload far too big and structured for a URL. So everyone reached for POST, exactly as you show, and threw away the three properties the operation actually has. It is safe, it is idempotent, and its result is cacheable, and POST asserts none of those to any intermediary in the path.
Your line that the two guarantees are what everything is built on lands hard on the AI side specifically. Because the method is safe and idempotent and the request content is part of what identifies the request, an identical query is genuinely cacheable, body and all, not only when the params happen to fit in a query string. For anyone chasing reproducible retrieval, same query in, same documents out, that is not a nicety, it is the method finally matching the semantics: a cache key that includes the actual query is how you get a deterministic read instead of a POST that every cache and proxy treats as an unrepeatable write.
The place it goes next, past your examples, is the agent layer. An MCP or A2A tool that fetches data is a query, not a mutation, and until now the transport could not say so, so an agent's reads and writes looked identical to everything sitting between it and the data. Giving retrieval its own safe, idempotent, body-carrying method fixes that quietly. The RFC reads like plumbing, but for retrieval-heavy and agent systems it closes a semantics mismatch a lot of us have been paying for in lost caching and dishonest idempotency.
The biggest benefit is probably semantic honesty. When search-like operations are forced through POST or overloaded GET, caching, observability, and intent all get muddier. A real method gives infrastructure something clearer to reason about.