Skip to main content
BlogBackend

HTTP QUERY Method: The Safe GET With a Body (RFC 10008)

HTTP finally has a method for search that needs a body. QUERY (RFC 10008) is safe, idempotent, and cacheable like GET — here is why it beats GET-with-a-body and POST.

Matthews Wong

Software Engineer

July 202610 min read
HTTP QUERY Method: The Safe GET With a Body (RFC 10008)

Photo by Taylor Vick on Unsplash

Key Takeaway

The HTTP QUERY method, standardized in RFC 10008 in June 2026, is a request that carries a body like POST but is safe, idempotent, and cacheable like GET. It exists so search and filter APIs can send complex queries in the body without abusing GET-with-a-body or losing caching by falling back to POST.

Every backend engineer eventually hits the same wall: a search endpoint whose parameters no longer fit in a URL. A faceted product filter, a GraphQL-style field selection, a geospatial polygon — the query is structured data, not a handful of string parameters. HTTP has historically given you two poor options for that, and in June 2026 the IETF finally shipped a third.

That third option is QUERY, published as RFC 10008, The HTTP QUERY Method. It grew out of the working-group draft named draft-ietf-httpbis-safe-method-w-body, and that draft name is the whole idea in one phrase: a safe method that is allowed to carry a body. Before we get to QUERY, it is worth being precise about why the two existing options hurt.

GET with a body was never safe

The instinct is to put the body on a GET, since a search reads data and GET is the read method. The specification disagrees. RFC 9110, the core HTTP semantics document, states plainly that 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. In practice: some proxies silently drop the body, and some treat it as an attack. A request that behaves differently depending on which intermediary it passes through is not something you can ship.

GET /contacts?select=surname,givenname,email&limit=10&match=%22email%3D*%40example.*%22 HTTP/1.1
Host: example.org

# RFC 9110 §9.3.1: content in a GET request "has no generally
# defined semantics" and may be rejected as request smuggling.

POST for search: it works, but gives up too much

So most of us reach for POST. It carries any body you like and every client and proxy understands it. The problem is what POST means. POST is neither safe, idempotent, nor cacheable — it is defined as a method that may change server state. That single definition costs you three concrete things. A dropped connection cannot be retried automatically, because nothing guarantees the first attempt did not already mutate something. A CDN or shared cache cannot reuse the response. And a reader of your logs cannot tell a state-changing write apart from a read that merely happens to carry a big body. You end up documenting this POST is really a read in prose that no cache or proxy will ever read.

QUERY: a body like POST, guarantees like GET

QUERY resolves the tension. RFC 10008 defines it as a method that asks the target resource to process the enclosed request content in a safe and idempotent manner and return the result. The content — JSON, form-encoded, whatever media type you declare — is the query. Because the method is defined as safe, the server promises the request changes no state; because it is idempotent, a client or proxy may retry it after a network blip without fear; and because the response is cacheable, a shared cache may reuse it for later identical QUERY requests. It is, quite literally, the read semantics of GET with the request body of POST.

QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: application/json
Accept: application/json

{ "select": ["surname","givenname","email"], "limit": 10, "match": "email=*@example.*" }

HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: /contacts/responses/42

[ { "surname": "Smith", "givenname": "John", "email": "smith@example.org" } ]

A 2xx QUERY response can carry a Content-Location header pointing at a results resource. RFC 10008 describes this as the server's claim that a client may send a plain GET to that URI to retrieve the same results — giving you a second, GET-cacheable handle on the query output for clients and CDNs that only know how to cache GET.

Safe, idempotent, cacheable — what each guarantee buys you

These three words are not decoration; each one unlocks a specific behavior in the machinery between your client and your server.

  • Safe means intermediaries and the client know the request is read-only, so prefetching, link-following, and automated retries are all legitimate — the same freedom they already grant GET.
  • Idempotent means a request that fails mid-flight can be retried automatically, because repeating it has the same effect as sending it once. This is the guarantee POST can never give a search.
  • Cacheable means a shared cache may store the response and serve it to the next identical query, cutting origin load on hot searches without you building a bespoke cache layer.

The cache key lives in the body

There is one genuinely new thing to get right. For GET, the cache key is the URI. For QUERY, the query is in the body, so the URI alone is not enough. RFC 10008 requires that the cache key for a QUERY request incorporate the request content and related metadata, not just the target URI. In practice a cache must hash the body — respecting content type and relevant headers — to distinguish two different searches sent to the same path. Caches that support QUERY do this for you; the point is that a naive URI-only cache will happily return the wrong results if you bolt QUERY onto it without support.

Do not enable QUERY caching on infrastructure that keys purely on the URL. A load balancer or edge cache that ignores the body will serve one search's results for a completely different search sent to the same path. Confirm your cache understands QUERY's body-inclusive cache key before turning caching on — otherwise leave it off and treat QUERY as an uncached safe read.

PropertyGET (with body)POSTQUERY
Request bodyUndefined by the specFully supportedDefined as the query
Safe (read-only)YesNoYes
Idempotent / auto-retryYesNoYes
Response cacheableYesNoYes
Works across proxies todayNo — body often droppedYesEmerging (RFC 10008)

Can you use it today?

QUERY is a Proposed Standard, not a future promise, but tooling is still catching up — so the honest answer is on the server, increasingly yes; in the browser, not yet.

  • The .NET 10 and ASP.NET Core 10 stack ships first-class support: HttpMethod.Query on the client and HttpMethods.Query with an IsQuery helper on the server, so you can route QUERY requests today.
  • Browsers cannot send QUERY from fetch or XMLHttpRequest yet, and MDN has no dedicated QUERY page — only a tracking issue — so public web clients still need a fallback.
  • The pragmatic pattern for now is service-to-service QUERY with a parallel POST search endpoint for browsers, sharing the same handler, until client support lands.

I would not rip out a working POST /search tomorrow. But if you are designing a new internal search or filter API between services you control, QUERY is the correct method — it finally lets a read that needs a body stay a read, all the way down through every cache and proxy on the path. Reach for it there first, keep POST as the browser-facing fallback, and let the ecosystem catch up around you.

FAQ

Frequently Asked Questions

Short answers to what readers ask most about this topic.

What is the HTTP QUERY method?

QUERY is an HTTP request method, standardized in RFC 10008 in June 2026, that asks a server to process content carried in the request body and return the result. It is essentially a safe GET with a body: the parameters travel in the body instead of the URI, but the request keeps GET's read-only guarantees. It was defined by the IETF HTTP Working Group in the draft named draft-ietf-httpbis-safe-method-w-body.

Is the HTTP QUERY method cacheable?

Yes. RFC 10008 states that a QUERY response is cacheable and a cache may reuse it to satisfy later QUERY requests. The key difference from GET is that the cache key must incorporate the request body and related metadata, not just the URL, because the query itself lives in the body. A server can also return a Content-Location header pointing to a GET-able results resource for conventional caching.

How is QUERY different from GET with a request body?

GET can technically carry a body, but RFC 9110 says that content in a GET request has no generally defined semantics and may even be rejected as a possible request-smuggling attack. QUERY was created precisely to remove that ambiguity: it explicitly defines that the request body is the query. So QUERY gives you a portable, well-specified, interoperable way to send a body on a safe request, which GET never had.

Can I use the HTTP QUERY method today?

Partially. Server frameworks are starting to support it: .NET 10 and ASP.NET Core 10 expose HttpMethod.Query on the client and HttpMethods.Query with an IsQuery helper on the server. However, browsers cannot yet send QUERY via fetch or XMLHttpRequest, and MDN has no dedicated page for it. For now QUERY is best suited to service-to-service APIs, with a parallel POST endpoint for public and browser clients.

Should I use QUERY or POST for a search endpoint?

POST works for complex search bodies, but it is neither safe, idempotent, nor cacheable, so proxies and caches cannot safely retry or reuse the response. QUERY carries the same body but is defined as safe and idempotent, so failed requests can be retried automatically and responses can be cached. Use QUERY for read-only searches and keep POST for operations that actually create or change state.