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/jsonsays the query content is JSON.Accept: application/jsonsays 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.
Advertise support before a client sends content
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.
Locationcan identify an equivalent resource. A later GET to that URI repeats the query without resending its content.Content-Locationcan 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:
- Verify every client can send an extension method with request content.
- Verify proxies, load balancers, WAF rules, and application routing preserve QUERY and its content.
- Test
OPTIONS,Allow,Accept-Query, CORS preflight, redirects, retries, conditional requests, and cache keys end to end. - Decide whether unsupported clients receive a documented GET or POST fallback.
- 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.
