What is the HTTP QUERY method

The QUERY method is an HTTP request that sends the query in the request body while keeping GET’s safety guarantees.
QUERY = GET with a body. Safe, idempotent, cacheable, but with a request body. The POST /search hack resolved at the protocol level.
sidebar: label: Introduction order: 1
What the RFC says
Section titled “What the RFC says”RFC 10008, published June 15, 2026. First new HTTP method since PATCH (RFC 5789, 2010). Sixteen years without a new method.
“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.”
Authors: Julian Reschke (greenbytes), James Snell (Cloudflare), Mike Bishop (Akamai).
The spec started life as SEARCH in 2015. Renamed November 2021 to escape WebDAV baggage. Eleven years from first draft to standard.
sidebar: label: Introduction order: 1
Method properties
Section titled “Method properties”| Property | Value | What it means in practice |
|---|---|---|
| Safe | Yes | No server state changes. Proxies and crawlers can fire it freely. |
| Idempotent | Yes | Same request, same result. Automatic retry is safe. |
| Cacheable | Yes | Responses can be cached. Cache key includes body + Content-Type. |
| Request body | Expected | The body IS the query. Content-Type is mandatory. |
The body defines the question. The Content-Type defines the question’s format. Servers MUST reject requests with missing or inconsistent Content-Type.
Discovery: Accept-Query
Section titled “Discovery: Accept-Query”Servers advertise support via the Accept-Query header:
Accept-Query: application/json, application/graphqlsidebar: label: Introduction order: 1
GET vs POST vs QUERY
Section titled “GET vs POST vs QUERY”| GET | POST | QUERY | |
|---|---|---|---|
| Safe | ✅ | ❌ | ✅ |
| Idempotent | ✅ | ❌ | ✅ |
| Cacheable | ✅ | Only for future GET/HEAD | ✅ |
| Request body | No defined semantics | Expected | Expected |
| Auto-retry | ✅ | ❌ (may cause side effects) | ✅ |
| Practical limit | ~8,000 octets in URL | No limit | No limit |
| Log leakage | Query params in access logs | Body stays out of logs | Body stays out of logs |
The GET problem
Section titled “The GET problem”RFC 9110 recommends implementations support URIs of at least 8,000 octets. That’s a floor, not a guarantee. Every proxy, load balancer, and server in the chain has its own limit. You discover the smallest one at runtime — usually via a 414 behind a corporate proxy your test suite will never reproduce.
URLs also leak. Access logs, bookmarks, browser history. If the query contains sensitive data, it’s now in every log along the path.
The POST problem
Section titled “The POST problem”POST works for queries until you need retries, caching, or honest semantics.
POST is not safe. POST is not idempotent. When the connection drops mid-request, no client or proxy knows whether state changed on the server. Nothing retries automatically. You end up building idempotency keys by hand — machinery that reads should never have needed.
Caching? RFC 9110 allows caching POST responses only with explicit freshness plus Content-Location matching, and even then it only serves future GET/HEAD. Two identical POST /search calls always hit your origin.
sidebar: label: Introduction order: 1
When to use it
Section titled “When to use it”- Complex filters that won’t fit in the URL (nested JSON, arrays, date ranges)
- Queries with sensitive data that shouldn’t leak into logs
- Search endpoints currently abusing POST for reads
- JSON-RPC and APIs where every call is semantically a read
- Any read-only request that needs a body
When NOT to use it
Section titled “When NOT to use it”- Simple queries that fit comfortably in the URL — GET is still better (simpler, universally supported)
- State-changing operations — use POST/PUT/PATCH/DELETE
- Public APIs where legacy clients don’t support QUERY — keep POST as a fallback
- Cross-origin browser requests where the preflight OPTIONS round-trip is unacceptable for your latency budget
sidebar: label: Introduction order: 1
Caching: how it works
Section titled “Caching: how it works”The QUERY response is cacheable, but the cache key MUST incorporate the request content, not just the URI. Section 2.7 of the RFC is explicit.
This is harder than caching GET. The cache must buffer the entire body before deciding whether it’s seen this request before. Caches may normalize (reorder JSON keys, strip whitespace) to improve hit rates.
The risk: over-aggressive normalization produces false positives — the cache returns the wrong query’s results.
Conditional requests work via ETag and If-None-Match, operating against the “equivalent resource” — a hypothetical GETable resource derived by incorporating the request body into the target.
sidebar: label: Introduction order: 1
A real example
Section titled “A real example”curl -X QUERY 'http://localhost:3000/contacts' \ -H 'Content-Type: application/json' \ -d '{"filter": {"city": "Berlin"}, "limit": 50}'What used to be:
GET /contacts?filter[city]=Berlin&limit=50Or, when it got too long:
POST /contacts/searchContent-Type: application/json
{"filter": {"city": "Berlin", "age_gte": 25, "tags": ["vip", "active"]}, "limit": 50}Now has the right method: a read that behaves like a read.
sidebar: label: Introduction order: 1
Current support (July 2026)
Section titled “Current support (July 2026)”| Stack | Status |
|---|---|
| Node.js | Native since Node 21.7.2 / 22+ (llhttp 9.2.0) |
| Fastify | fastify.addHttpMethod('QUERY', { hasBody: true }) |
| Go net/http | Works with arbitrary method strings |
| .NET | .NET 11 Preview 4 recognizes QUERY |
| Spring | PR #34993 open, blocked |
| OpenAPI | 3.2.0 (Sep 2025) — first-class query field |
| curl | -X QUERY with --data and explicit Content-Type |
| Browsers | fetch() accepts it, but CORS preflight mandatory |
CDNs: two of the RFC’s three authors work at Cloudflare and Akamai. Good sign. Official edge cache support? Not confirmed yet.
sidebar: label: Introduction order: 1