📑 Contents Overview / TL;DR Why It Was Needed Syntax & Basic Example Safety & Idempotency Content-Type & Errors Accept-Query & Discovery Caching Semantics Location & Equivalent Resource Redirects & Conditionals GET vs QUERY vs POST Security Considerations Ecosystem & Adoption (2026) Using QUERY in Code When to Use It References

1. Overview / TL;DR

QUERY is a new HTTP request method, standardized in RFC 10008 (published June 2026 by J. Reschke, J.M. Snell, and M. Bishop under the IETF HTTPBIS working group). It sits deliberately between GET and POST: like POST, it carries a request body; like GET, it is explicitly safe and idempotent, so it can be cached, retried, and prefetched automatically.

💡

One-line definition: QUERY asks the target resource to process the enclosed request content as a read-only query and return the results — without the URI-length limits of GET or the "did this mutate something?" ambiguity of POST.

GET
  • ✅ Safe & idempotent
  • ❌ No request body semantics
  • Query lives in the URI
QUERY
  • ✅ Safe & idempotent
  • ✅ Request body carries the query
  • URI is optional / a resource path
POST
  • ⚠️ Not safe, not idempotent by contract
  • ✅ Request body
  • Semantics are opaque to intermediaries

This page is a from-source deep dive: it pulls directly from the RFC text and the working-group drafts that preceded it, plus the practical ecosystem status as of mid-2026 (which frameworks and servers actually support it, and what still needs manual wiring).

2. Why It Was Needed

Every API that supports rich filtering, search, or reporting eventually runs into the same fork: encode the query in the URI (GET), or smuggle it into a request body (POST). RFC 10008 spells out why both are unsatisfying.

The problem with GET + URI query parameters

  • Unpredictable size limits — a request can pass through many uncoordinated intermediaries (proxies, load balancers, CDNs, WAFs), each with its own undocumented URI length cap. There is no reliable way to know in advance whether a complex query will fit.
  • Inefficient encoding — structured or nested data (arrays of objects, boolean expressions, full-text search syntax) is awkward and verbose once flattened into application/x-www-form-urlencoded key/value pairs.
  • Privacy / logging exposure — request URIs are logged far more readily than request content by proxies, servers, browser history, and analytics tools, and they show up in bookmarks. A URI-encoded query leaks more than the equivalent content in a body.
  • Resource identity blow-up — under strict HTTP semantics, every distinct query string is technically a distinct resource identifier, which sits awkwardly with how people actually think about "the same search, different parameters."

The problem with POST

POST has no fixed safety or idempotency contract — RFC 9110 explicitly leaves both open. That means:

  • A client (or an intermediary, or a cache) cannot tell, just from the method, whether a given POST is a harmless read or a state-changing write.
  • Automatic retries are unsafe by default — if a connection drops mid-request, blindly resending a POST risks double-processing a mutation.
  • Caches generally treat POST responses as non-cacheable or only cacheable under narrow, explicit conditions.

Earlier attempts to close this gap — SEARCH, REPORT, PROPFIND — all originate from the WebDAV activity and tie their semantics to a generic application/xml body, with "mixed feelings" (the RFC's own words) about reusing WebDAV machinery for general-purpose APIs. QUERY was designed as a clean, media-type-agnostic alternative whose name directly captures its relationship to the URI's query component.

3. Syntax & Basic Example

A QUERY request looks exactly like a POST at the wire level, except for the method token and the semantics that come with it. The request content is the query; its media type tells the server how to interpret it.

Request
QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded
Accept: application/json

select=surname,givenname,email&limit=10&match=%22email=*@example.*%22
Response
HTTP/1.1 200 OK
Content-Type: application/json

[
  { "surname": "Smith",
    "givenname": "John",
    "email": "smith@example.org" },
  { "surname": "Jones",
    "givenname": "Sally",
    "email": "sally.jones@example.com" }
]
ℹ️

The request content is not tied to any single format. application/x-www-form-urlencoded, JSON query DSLs, GraphQL documents, SQL fragments, or a custom application/jsonpath media type are all valid — the server just has to declare (and enforce) what it accepts.

The request URI still matters: it identifies the target resource the query is run against (here, /contacts), the same way a POST target identifies where data is submitted. What's new is that the body's job is unambiguous — it is the query, not "maybe a query, maybe a mutation, depends on the endpoint."

4. Safety & Idempotency

These two properties, inherited from RFC 9110's definitions, are the entire reason QUERY is useful to caches, proxies, and retry logic.

Safe

"QUERY requests are safe with regard to the target resource; that is, the client does not request or expect any change to the state of the target resource." A server that mutates state in response to a QUERY is violating the contract, not exercising a valid edge case.

Idempotent

"QUERY requests are idempotent; they can be retried or repeated when needed, for instance, after a connection failure." N identical requests have the same effect (none) as one.

Practically, this unlocks behavior that was never safe to apply to a generic POST:

  • HTTP clients and load balancers can automatically retry a failed QUERY the same way they retry a GET, without a "was this already applied?" concern.
  • Caches are explicitly permitted to store and reuse QUERY responses (see §7).
  • Tooling (linters, API gateways, WAFs) can treat QUERY as read-only traffic for rate-limiting, auditing, and access-control purposes without inspecting the body's business logic.

5. Content-Type & Error Handling

Because the body is the query, the media type is not optional decoration — it is load-bearing. The RFC is blunt about this:

⚠️

"Servers MUST fail the request if the Content-Type request field is missing or is inconsistent with the request content."

The spec gives explicit guidance on which status code to use for which failure mode:

StatusMeaningExample trigger
400Bad RequestMissing Content-Type, or content doesn't match the declared type
415Unsupported Media TypeThe resource doesn't understand this query format at all
422Unprocessable ContentSyntactically valid query, semantically invalid (e.g. references a non-existent field or table)
406Not AcceptableClient's Accept header requests a response format the server can't produce

This is a meaningfully richer error vocabulary than a typical GET-with-query-string endpoint, which usually collapses "malformed query" and "query about something that doesn't exist" into an undifferentiated 400.

6. Accept-Query & Discovery

A client needs a way to find out, ahead of time, whether a resource supports QUERY at all and which query formats it accepts. RFC 10008 defines a new response header for exactly this, Accept-Query, encoded as an HTTP Structured Field (RFC 9651) of type List.

Discovery via HEAD
HEAD /contacts HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Accept-Query: "application/x-www-form-urlencoded", "application/sql"
Discovery via OPTIONS (standard Allow header)
OPTIONS /contacts HTTP/1.1
Host: example.org

HTTP/1.1 200 OK
Allow: GET, QUERY, OPTIONS, HEAD

Media types in Accept-Query can carry parameters and wildcards (*/* or type/*), the same as a client's own Accept header. One subtlety worth internalizing: the value of Accept-Query applies to every URI that shares the same path — the query component of the URI itself is ignored for this purpose, since QUERY's own request content is what varies, not the URI.

7. Caching Semantics

Because QUERY is safe, its responses are explicitly cacheable — but the cache key can no longer be just the URI, since the query lives in the body.

🗄️

"The response to a QUERY method is cacheable; a cache MAY use it to satisfy subsequent QUERY requests. […] The cache key for a QUERY request MUST incorporate the request content and related metadata."

Caches are allowed to normalize semantically-insignificant differences before computing that key — stripping content-encoding, or normalizing format conventions such as a +json media-type suffix — but any such transformation exists purely to compute the cache key. It never changes the actual request sent onward. The RFC also flags the obvious risk here: a cache that normalizes incorrectly, or too aggressively, can serve a stale or simply wrong response if two content bodies that aren't really equivalent get folded into the same key (see §9).

⚠️

Mid-2026 reality check: most deployed CDN and reverse-proxy caching logic predates QUERY and has no concept of "hash the body into the cache key." Expect QUERY responses to pass through as effectively uncached until cache infrastructure catches up (see §10).

8. Content-Location, Location & Equivalent Resource

QUERY introduces the concept of an equivalent resource: a plain GET-able resource that represents a particular QUERY request (its target plus its content) and can be re-fetched with a normal, bodyless GET. Servers aren't required to expose this, but if they do, they signal it with response headers.

Content-Location — "here's a URI for exactly these results"

HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: /contacts/stored-results/17

[ ...results... ]

This is a claim that a client can GET /contacts/stored-results/17 later to retrieve this specific result set again.

Location — "here's a URI that re-runs the same query"

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

[ ...results... ]

This is a claim that a plain GET /contacts/stored-queries/42 will repeat the query operation itself — useful for bookmarking a saved search or sharing a link without re-sending the original request body.

🔒

If the original query content was sensitive (e.g. contained a customer's email or an internal ID), the RFC explicitly warns: the assigned URI SHOULD NOT encode any sensitive portion of that content, since URIs are logged far more readily than bodies.

9. Redirects & Conditional Requests

Redirect status codes

  • 301 / 308 (permanent) and 302 / 307 (temporary) — the server suggests the client repeat the same kind of request (another QUERY, with the same content) against the new Location. Unlike POST, a QUERY does not silently downgrade to GET after one of these.
  • 303 See Other — signals that the query's results can be fetched with a plain GET against the URI in Location, without resending the original content.

Conditional requests

The "selected representation" for a QUERY is defined the same way as for an equivalent GET, so standard conditional headers — If-Modified-Since, If-None-Match, etc. — work as expected: a conditional QUERY only returns a body when the condition is met.

Range requests

Byte-range semantics carry over unchanged from GET, but the RFC is candid that they're rarely useful here — query result formats typically define their own pagination (SQL's FETCH FIRST … ROWS ONLY, or a JSON API's limit/offset), and clients are expected to use those instead of HTTP byte ranges.

10. GET vs QUERY vs POST

PropertyGETQUERYPOST
Safeyesyesnot guaranteed
Idempotentyesyesnot guaranteed
Request body semanticsundefineddefines the querydefined per-endpoint
URI identifies the query itselfyes, by definitionoptional (via Location)no
Cacheableyesyes (body-aware cache key)limited / opt-in
Auto-retry safeyesyesno
CORS preflight requirednoyes (not a safelisted method)no (for simple requests)
URI logging / privacy exposurehigh (query in URI)low (query in body)low

Why not reuse SEARCH, REPORT, or PROPFIND?

These predate QUERY and can express similar ideas, but the working group rejected reusing them for two reasons stated directly in the spec: they originate from WebDAV, which the wider HTTP community has "mixed feelings" about depending on outside a WebDAV context; and they tie their semantics to a single generic application/xml body, whereas QUERY is media-type-agnostic — the query format is a first-class, negotiable property of the request (see §6), not baked into the method's own spec.

11. Security Considerations

  • 1
    Reduced URI-logging exposure. Moving the query from the URI to the body means less sensitive data ends up in access logs, proxy logs, browser history, and bookmarks — a genuine privacy improvement over GET-with-query-string for anything sensitive (emails, account IDs, free-text search of PII).
  • 2
    Temporary resource naming. If a server mints a URI for a QUERY's results (Content-Location/Location) and the original query contained sensitive content that can't be logged, that URI SHOULD be chosen so it doesn't itself leak the sensitive parts of the request.
  • 3
    Cache-key normalization risk. A cache that normalizes QUERY bodies incorrectly — treating two subtly different queries as equivalent — can return a wrong (stale or mismatched) result. This is a new class of cache-poisoning-adjacent bug that didn't exist when caches only ever keyed on the URI.
  • 4
    Mandatory CORS preflight. QUERY is not on the fetch spec's list of CORS-safelisted methods, so any cross-origin browser request triggers an OPTIONS preflight — same operational cost as a custom-method or non-simple-content-type POST/PUT/DELETE today.
  • 5
    It's still an injection surface. QUERY does nothing to make a query format safe by itself — a QUERY body containing a raw SQL fragment needs exactly the same parameterization/escaping discipline as any other user-supplied query text. Treat it as untrusted input at the system boundary, same as a GET query string or a POST body.
  • 12. Ecosystem & Adoption Status (mid-2026)

    RFC 10008 is a finished Proposed Standard, but standardization and ecosystem support are two different clocks. As of mid-2026, here's the honest state of things:

    LayerStatusNotes
    Node.js core (http/https)supportedLanded in Node 21.7.2 — the low-level HTTP client/server can send and receive arbitrary methods including QUERY.
    Express.jsmanual routingNo first-class app.query() yet — tracked in an open GitHub issue. Custom methods can be routed today via app.use() + a method check, or app.all() with manual dispatch.
    nginx / Apachepasses through, needs configBoth proxy QUERY through by default, but anything that allow-lists methods — limit_except blocks, WAF rules, security middleware — needs QUERY added explicitly or the requests get rejected.
    Browser fetch()already worksNo spec change needed — fetch(url, { method: 'QUERY', body }) already sends an arbitrary method today; it just wasn't a named, standardized method with defined semantics until now.
    curlalready workscurl -X QUERY -d '...' https://example.org/contacts — curl has always allowed arbitrary -X method overrides.
    CDNs / edge cachesnot cache-aware yetMost CDN cache-key logic still only considers the URI. A QUERY response is typically treated as an unrecognized method and passed straight through uncached until providers add body-aware cache keys.
    🧭

    Practical takeaway: the wire protocol and the "can my HTTP client send this" question are already solved everywhere. The gap is entirely in server-side framework ergonomics (routing sugar) and shared caching infrastructure (CDNs, reverse proxies) — expect first-class framework support to roll out through 2026–2027 as adoption pressure builds.

    13. Using QUERY in Code

    Client: browser fetch()

    const res = await fetch('/api/products', {
      method: 'QUERY',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ category: 'shoes', maxPrice: 100, sort: '-rating' })
    })
    const products = await res.json()

    Client: curl

    curl -X QUERY https://shop.example.com/products \
      -H 'Content-Type: application/x-www-form-urlencoded' \
      -d 'category=shoes&maxPrice=100&sort=-rating'

    Client: Node.js (core http module)

    import http from 'node:http'
    
    const req = http.request('http://example.org/contacts', {
      method: 'QUERY',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
    }, (res) => {
      let data = ''
      res.on('data', chunk => data += chunk)
      res.on('end', () => console.log(JSON.parse(data)))
    })
    req.end('select=surname,email&limit=10')

    Server: Express (manual routing until native support lands)

    Express doesn't yet expose app.query(), but the method still arrives on req.method, so it can be dispatched by hand:

    app.use('/contacts', (req, res, next) => {
      if (req.method !== 'QUERY') return next()
    
      if (!req.is('application/x-www-form-urlencoded')) {
        return res.status(415).send('Unsupported query format')
      }
    
      const { select, limit } = req.body // parsed by a urlencoded body parser
      const results = runContactQuery({ select, limit })
    
      res.set('Accept-Query', 'application/x-www-form-urlencoded')
      res.json(results)
    })
    ⚠️

    Remember to add QUERY to any CORS Access-Control-Allow-Methods list and to any reverse-proxy / WAF method allow-list — since it's a new token, anything that whitelists HTTP methods needs to be told about it explicitly, or requests will be rejected before they reach your application code.

    14. When to Use QUERY vs GET vs POST

    ✅ Reach for QUERY when…

    The operation is genuinely read-only and the query is complex, nested, potentially large, or sensitive enough that you don't want it sitting in a URI — full-text search, GraphQL-style queries, SQL-like filters, faceted search with many parameters.

    Stick with GET when…

    The query is small, simple, and shareable-as-a-link is a feature you want (pagination, a handful of filter flags) — GET's URI-as-resource-identifier property is a genuine strength for bookmarkable, cacheable-by-CDN-today endpoints.

    Stick with POST when…

    The operation actually changes state — creates, updates, triggers a side effect — QUERY's safety contract makes it the wrong tool the moment a request has a mutating effect.

    Hold off on QUERY when…

    You need this to work through every consumer's toolchain today without any adjustment — third-party CDNs, older WAFs, and rigid corporate proxies with strict method allow-lists may still reject or mishandle it until it's more broadly configured for (see §12).

    References