Save products you love by clicking the heart icon.
HTTP QUERY Is Here: What RFC 10008 Means for API Design | Articles | Tobias Weiss
HTTP QUERY Is Here: What RFC 10008 Means for API Design
~10 min read
httpapi-designprotocolsrfcrestweb-standards
Sixteen Years Without a New Method
The last time the HTTP method registry grew was 2010, when PATCH (RFC 5789) arrived to solve the partial-update problem. Before that, you have to go back to the original HTTP/1.1 specification in 1997 for the core set — GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT. For nearly three decades, API designers have been forced to contort real-world use cases into a method set that was never designed for modern query-heavy, body-carrying workloads.
That changes with RFC 10008, published in June 2026 on the IETF Standards Track. Authored by Julian Reschke (greenbytes), James M. Snell (Cloudflare), and Mike Bishop (Akamai), the QUERY method fills a gap that has been a source of architectural friction since the first RESTful API was deployed.
The Problem That Wouldn't Die
Every API designer has faced this scenario: you need to send a complex query to a server — a GraphQL document, an Elasticsearch query DSL, a SPARQL triple pattern, a CouchDB _find selector. These payloads are too large for a GET query parameter, and they contain special characters that make URL encoding painful. Your options have been:
Method
Carries Body
Safe
Idempotent
Cacheable
GET
No
Yes
Yes
Yes
POST
Yes
No
No
Only by explicit header
QUERY
Yes
Yes
Yes
Yes (with body in key)
Safe means the request causes no side effects. Idempotent means N identical requests produce the same server state. GET is both — but cannot carry a body. POST can carry a body but is neither safe nor idempotent, which breaks retry logic, intermediary caching, and preloading. This is not a niche problem.
GraphQL deployments ship every query as a POST because the query document is a JSON string that lives in the request body. This means GraphQL queries cannot be cached by HTTP intermediaries, cannot be safely retried, and cannot be prefetched by browsers or CDNs. The GraphQL community has long known this is suboptimal — the recommended workaround is to compute a hash of the query and send it as a GET parameter, but that requires server-side support and breaks the self-describing nature of the request.
Elasticsearch users deal with the same tension: every search query is a POST to _search. So do SPARQL endpoints, OData services, and CouchDB clients. The QUERY method eliminates this category of workaround entirely.
How QUERY Works
RFC 10008 defines QUERY as a method that:
Is safe — the server MUST NOT have side effects from a QUERY request
Is idempotent — multiple identical requests produce the same result
Carries a request body — the semantics of the query are expressed in the payload
Is cacheable — responses MAY be stored and reused by caches
Supports content negotiation — the client indicates what query language it speaks via the Content-Type header
The request body contains the query expression. The server interprets it according to the media type in Content-Type and returns a representation of the results. The response can be any HTTP status code appropriate for the query — 200 for a successful result set, 204 for empty results, 404 if the query targets a resource that doesn't exist.
The Cache Key Problem
One of the trickiest design questions the RFC had to solve was caching. A QUERY to /api/search with {"term": "kubernetes"} should produce a different cached response than the same URL with {"term": "rust"}. In HTTP, the cache key for GET requests is the URL alone. For QUERY, the cache key MUST include the request body.
The RFC specifies that caches calculate the cache key as the URL plus the body content. Vary headers and Cache-Control directives apply as normal. Intermediate caches that do not support body-in-cache-key MUST NOT cache QUERY responses. This is a significant implementation hurdle for existing CDNs and reverse proxies — more on that below.
The Accept-Query Header
Servers that support QUERY advertise it via the Accept-Query response header (or OPTIONS response). This header lists the query media types the server understands:
Clients can use OPTIONS to discover whether a server supports QUERY before sending a potentially expensive request. This mirrors how Allow works for method discovery.
Why Not SEARCH?
If you have been around the WebDAV world, you might recall the SEARCH method defined in RFC 5323. It had essentially the same semantics — safe, idempotent, carries a body. So why a new method?
The working group considered SEARCH but ultimately rejected it for two reasons:
SEARCH carried WebDAV baggage — the method was tied to WebDAV's XML-based query syntax and had implementation assumptions that didn't map cleanly to JSON, GraphQL, or other modern formats
The name "SEARCH" implies narrow semantics — QUERY is broader; it includes search, but also introspection, schema discovery, validation, and any operation that asks the server a question
The consensus was that a clean start was preferable to retrofitting SEARCH for the modern web.
Real-World Adoption
Adoption is happening faster than any previous HTTP method. Here is where things stand as of July 2026:
Client Libraries
Runtime
Support
Notes
Node.js 21.7+
Native via http module
Ships in core — no polyfill needed
curl
--request QUERY
Full support since 8.10
Rust reqwest
Custom method via Method::from_bytes(b"QUERY")
Works with any HTTP method
Python httpx
client.request("QUERY", url, content=body)
Supported via generic method API
Go net/http
No special const, but http.NewRequest("QUERY", url, body)
Laravel deserves special mention — Taylor Otwell shipped Route::query() in Laravel 12 within weeks of RFC publication, making it the first major framework to provide first-class QUERY method routing.
Middleboxes and CDNs
This is where the story gets complicated:
Proxy
Status
nginx
Passes through unmodified — supports unknown methods by default
Passes through but ignores body in cache key — effectively uncacheable
Varnish
Requires custom vcl to handle body-based cache key
If you are behind CloudFront or Akamai, you cannot serve QUERY requests through the CDN today. This is the single biggest adoption blocker. The RFC authors are actively working with CDN providers, and Cloudflare has indicated support will ship before the end of 2026.
import http from "node:http";
const server = http.createServer((req, res) => {
if (req.method === "QUERY" && req.url === "/search") {
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", () => {
const query = JSON.parse(body);
// Execute search against index
const results = searchIndex(query.term, query.limit);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(results));
});
return;
}
// Handle other methods...
});
server.listen(3000);
Go — Minimal QUERY Handler
package main
import (
"encoding/json"
"net/http"
)
type Query struct {
Term string `json:"term"`
Limit int `json:"limit"`
}
func queryHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "QUERY" {
http.Error(w, "method not allowed", 405)
return
}
var q Query
json.NewDecoder(r.Body).Decode(&q)
results := search(q.Term, q.Limit)
json.NewEncoder(w).Encode(results)
}
func main() {
http.HandleFunc("/api/search", queryHandler)
http.ListenAndServe(":8080", nil)
}
Where QUERY Shines
GraphQL Endpoints
This is the killer use case. A GraphQL endpoint today sends every query as POST. With QUERY:
Queries become cacheable — CDNs and reverse proxies can store the response keyed by URL + query document
Queries become safe — the framework can guarantee no mutations happen through the QUERY method
Mutations still use POST — the distinction is now enforced by the protocol, not convention
A GraphQL server advertising Accept-Query: application/graphql tells clients "you can send read-only queries here." The framework can route QUERY requests to the query executor and POST requests to the mutation executor at the transport layer, eliminating an entire class of bugs where a query accidentally triggers a write.
Search APIs
Elasticsearch, Algolia, Meilisearch, and every custom search endpoint benefits immediately. Search is inherently safe and idempotent — you are asking the index a question. QUERY makes this explicit in the protocol.
SPARQL and OData
Triple stores and OData services have always carried complex query expressions in the body. SPARQL endpoints in particular have suffered from GET URL length limits (the default SPARQL protocol over GET encodes the entire query in the URL). QUERY eliminates this constraint entirely.
CouchDB _find
CouchDB is a case study in method workarounds. Its _find endpoint uses POST with a JSON selector, and the CouchDB community has long wanted a dedicated method. In fact, the CouchDB team registered ? as an HTTP method back in 2015, waiting for a standard — QUERY is the formalisation they were waiting for.
Caveats and Migration
QUERY is not a drop-in replacement for existing patterns. Here are the considerations before adopting it:
CDN compatibility — If you rely on CloudFront or Akamai, you cannot use QUERY through them today. Plan for an upgrade path where QUERY is terminated at your origin or behind a proxy that supports it.
Framework support — Most web frameworks do not have dedicated QUERY routing yet. You can still handle it via generic method matching (r.Method == "QUERY"), but you lose the ergonomics that POST and GET have. This will improve over the next 12 months.
Existing clients — Old clients will still send POST to your query endpoints. You need to support both QUERY and POST during a transition period. The RFC recommends this explicitly — servers SHOULD continue to accept POST as a fallback for query operations.
Caching infrastructure — If you cache responses, ensure your cache layer supports body-in-cache-key. Varnish requires custom VCL. nginx requires the proxy_cache_key directive to include the body. Redis-based caches need to hash the body manually.
Security appliances — WAFs, API gateways, and intrusion detection systems may not recognise QUERY. Check your security tooling for method allowlist support and add QUERY explicitly.
The Shape of Things to Come
RFC 10008 is the first new HTTP method in sixteen years, but it will not be the last. The HTTP Working Group has resumed work on method registration with an eye toward formalising patterns that have emerged organically — WebSocket upgrade semantics, event-stream subscription patterns, and idempotent batch operations are all under discussion.
For API designers, QUERY offers something that has been missing since HTTP was first standardised: a method that is simultaneously safe, idempotent, body-carrying, and cacheable. The fact that it took thirty years to arrive says less about the difficulty of the problem and more about how creative the web community has been at working around protocol gaps. GraphQL, Elasticsearch, CouchDB, SPARQL, and every search API you have ever built were all using the wrong method because the right one did not exist.
Now it does. Start by adding Accept-Query to your OPTIONS response. Add QUERY routing alongside your existing POST handlers. Verify your CDN allows it. And the next time you design an API that asks the server a question, use the method that was designed for that purpose.