Use the HTTP QUERY method for safe requests with content

Use QUERY when a safe, idempotent operation needs structured request content that does not fit naturally in a URI.

  • methods
  • request content
  • safety
  • idempotency
  • caching

The gap between GET and POST

GET is a natural fit when the target URI identifies the resource to retrieve. Small query inputs often fit in the URI’s query component:

GET /records?status=active&limit=2 HTTP/1.1
Host: example.invalid

Expected result: the target URI identifies the requested resource, including the short query component.

That design becomes awkward when the input is large, structured, or sensitive enough that putting it in a frequently logged URI is undesirable. POST can carry content, but POST does not tell clients and intermediaries that the operation is safe and idempotent.

QUERY fills that gap. It asks a target resource to process enclosed query content without the client requesting a state change.

QUERY /records HTTP/1.1
Host: example.invalid
Content-Type: application/json
Accept: application/json

{"status":"active","limit":2}

Expected result: /records processes the JSON query safely and returns its result without interpreting the request as a state-changing operation.

The target /records defines the scope. The JSON media type and content define the query within that scope.

Compare GET, QUERY, and POST by contract

Property GET QUERY POST
Safe Yes Yes Not by definition
Idempotent Yes Yes Not by definition
Request content No generally defined semantics Expected; meaning defined by the target Expected; meaning defined by the target
Query identified by a URI Yes Optional through Location No
Response cacheable Yes Yes Only for later GET or HEAD reuse

Choose from the operation’s semantics, not from payload size alone. A request that creates, updates, deletes, triggers, or otherwise asks for application state to change is not QUERY. A safe operation with a short, stable URI can remain GET.

Define the query format explicitly

A QUERY request needs Content-Type. The server is not allowed to infer a missing or incorrect type by inspecting the content. Define which media types a resource accepts and what each type means for that resource.

For a JSON format, document the JSON shape as part of the resource contract:

{
  "status": "active",
  "limit": 2
}

Expected result: the resource validates this documented shape after confirming that Content-Type declares JSON.

Keep request-content negotiation separate from response-content negotiation:

  • Content-Type: application/json says the query content is JSON.
  • Accept: application/json says the client wants a JSON response.
  • Accept-Query: "application/json" says the resource accepts JSON query content.

Reject missing media type information with a suitable 4xx response. Use 415 when the declared query format is unsupported, 400 when declared metadata and content are inconsistent, 422 when valid query content cannot be processed, and 406 when no acceptable response format is available.

Clients should not have to guess whether an endpoint implements a new method. An OPTIONS response can advertise the method and its formats:

OPTIONS /records HTTP/1.1
Host: example.invalid

Expected result: the server reports which methods and query formats the resource supports.

HTTP/1.1 204 No Content
Allow: GET, HEAD, QUERY, OPTIONS
Accept-Query: "application/json"

Expected result: the client learns that the resource supports QUERY with JSON query content.

Allow states which methods the resource supports. Accept-Query both signals QUERY support and lists permitted query media types. Its value uses Structured Fields syntax, so do not parse it as if it were simply another Accept field.

Understand the equivalent resource

For each QUERY request, HTTP defines an equivalent resource that accounts for the target, request content, and relevant metadata. A server does not have to assign that resource a URI, but doing so can make results easier to reuse.

  • Location can identify an equivalent resource. A later GET to that URI repeats the query without resending its content.
  • Content-Location can identify a resource corresponding to the result in the current response.

These are separate claims. Do not add either field unless the advertised URI really supports the promised GET behavior. If query content is sensitive, never copy it into a generated URI.

Cache the request content as part of the key

QUERY responses are cacheable, but the cache key must include the query content and related metadata. Keying only by method and target URI could serve one query’s result for a different query, which is a serious correctness and privacy failure.

A cache can normalize semantically insignificant differences only when it understands the format and applies the same meaning as the resource. Otherwise, use the exact content and metadata. Providing a Location for an equivalent GET resource can shift later requests onto the simpler and more widely implemented GET cache path.

Conditional requests also apply. Validators describe the selected representation of the equivalent resource, so a repeated conditional QUERY can receive 304 Not Modified when the validator still matches.

Redirect QUERY deliberately

The historical POST-to-GET rewrite permitted for some 301 and 302 responses does not apply to QUERY. A client follows 301, 302, 307, or 308 with a similar QUERY request at the new target. Use 303 See Other when the intended next step is a retrieval, normally GET.

As with any redirect carrying request content, confirm client and intermediary behavior before relying on it in production.

Expect browser preflight

QUERY is not a CORS-safelisted method. A cross-origin browser request therefore performs an OPTIONS preflight before sending QUERY. JSON content also introduces the non-safelisted Content-Type: application/json value.

The preflight response needs an origin grant plus permissions such as:

Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Methods: QUERY
Access-Control-Allow-Headers: Content-Type

Expected result: the browser may send the actual QUERY request only when its origin, method, and non-safelisted content type match this grant.

The actual QUERY response also needs the applicable origin grant. A successful preflight does not remove CORS requirements from the actual response.

Roll out with capability checks

QUERY was standardized in 2026, so older frameworks, gateways, caches, security rules, SDKs, and observability pipelines might reject it or treat it as unknown. Before production use:

  1. Verify every client can send an extension method with request content.
  2. Verify proxies, load balancers, WAF rules, and application routing preserve QUERY and its content.
  3. Test OPTIONS, Allow, Accept-Query, CORS preflight, redirects, retries, conditional requests, and cache keys end to end.
  4. Decide whether unsupported clients receive a documented GET or POST fallback.
  5. Monitor method-specific 405, 415, and preflight failures during rollout.

A POST fallback can reproduce the operation, but it does not inherit QUERY’s standard safe and idempotent method properties. Treat fallback behavior as an explicit application contract.

Design checklist

Use QUERY when all of these statements are true:

  • The client is requesting a safe operation with no intended state change.
  • Repeating the same request has the same intended effect.
  • Structured request content is more appropriate than encoding the input in the URI.
  • The target resource defines the query format and processing scope.
  • Clients and the complete HTTP path support the method.
  • Caches incorporate content and related metadata into the key, or QUERY caching is disabled deliberately.
  • Browser deployments implement the required preflight and actual-response CORS fields.

If the first two statements are false, choose a method whose semantics match the state-changing operation instead.

Primary sources