RFC 10008

The HTTP QUERY Method

πŸ“… published June 2026, from ietf

RFC 10008: The HTTP QUERY Method πŸŽ€

okay bestie, let's set the scene πŸ’… this is RFC 10008, published by the Internet Engineering Task Force (IETF) in June 2026. It's a Standards Track document, which means it's the real deal and not a vibes-only draft ✨ The ISSN is 2070-1721 🌸 The authors are J. Reschke (greenbytes), J.M. Snell (Cloudflare), and M. Bishop (Akamai), so that's three brilliant brains on this one 🧠 The title block doesn't list anything it obsoletes or updates, so it isn't replacing or patching any other RFC.

Abstract ✨

okay so the abstract is basically the TL;DR πŸ¦‹ It defines the QUERY method for HTTP. A QUERY asks the request target to process the enclosed content in a safe and idempotent way, and then to respond with the result. It's giving POST vibes, but QUERY requests can be automatically repeated or restarted without worrying about partial state changes πŸ”

Status of This Memo 🌈

this is an Internet Standards Track document πŸ’–

this one is a product of the IETF, and it represents the consensus of the IETF community. It went through public review and got approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check out Section 2 of RFC 7841 πŸ“š

for the current status, any errata, and how to send feedback on this doc, go to https://www.rfc-editor.org/info/rfc10008 πŸ”—

Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.

this doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the publication date. Please read those carefully, because they spell out your rights and restrictions for this document. Code Components pulled out of this document have to include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Revised BSD License βš–οΈ

Table of Contents πŸ“š

   1.  Introduction
     1.1.  Terminology
     1.2.  Notational Conventions
   2.  QUERY Method
     2.1.  Media Types and Content Negotiation
     2.2.  Equivalent Resource
     2.3.  Content-Location Response Field
     2.4.  Location Response Field
     2.5.  Redirection
     2.6.  Conditional Requests
     2.7.  Caching
     2.8.  Range Requests
   3.  The Accept-Query Header Field
   4.  Security Considerations
   5.  IANA Considerations
     5.1.  Registration of the QUERY Method
     5.2.  Registration of the Accept-Query Field
   6.  References
     6.1.  Normative References
     6.2.  Informative References
   Appendix A.  Examples
     A.1.  Simple Query
     A.2.  Discovery of QUERY Support
     A.3.  Discovery of QUERY Formats
     A.4.  Content-Location, Location, and Indirect Responses
       A.4.1.  Using Content-Location
       A.4.2.  Using Location
       A.4.3.  Indirect Responses
     A.5.  Conditional Requests
     A.6.  More Query Formats
   Appendix B.  Selection of the Method Name 'QUERY'
   Acknowledgements
   Contributors
   Authors' Addresses

1. Introduction πŸ¦‹

so this spec defines the HTTP QUERY request method πŸ’… It's a way to make a safe, idempotent request (Section 9.2 of [HTTP]), and that request carries a representation that describes how the target resource should process it.

the classic query pattern looks like this πŸ‘€

GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
Host: example.org

but once the data is too voluminous to fit into the request's URI, this pattern gets messy 😭 Here's why:

  • size limits are often not known ahead of time, because a request can pass through lots of uncoordinated systems. (Side note: Section 4.1 of [HTTP] recommends that senders and recipients support at least 8000 octets πŸ“)
  • expressing certain kinds of data in the target URI is inefficient, because you have to do the overhead of encoding that data into a valid URI πŸ™ƒ
  • request URIs are more likely to get logged than request content, and they can also show up in bookmarks πŸ““
  • encoding queries directly into the request URI basically turns every possible combo of query inputs into its own distinct resource 🀯

so people often use HTTP POST for queries instead, like in this example 🫢 The query input goes in the request content, not in the request URI's query component.

POST /feed HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded

q=foo&limit=10&sort=-published

the problem is that, without specific knowledge of the resource and server you're talking to, it's not obvious that a safe, idempotent query is happening here πŸ™ˆ

enter QUERY 🌸 It sits right between GET and POST, and the example above becomes:

QUERY /feed HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded

q=foo&limit=10&sort=-published

just like POST, the query input rides in the request content instead of the URI. The big difference is that QUERY is explicitly safe and idempotent, which means things like caching and automatic retries can actually work πŸ”βœ¨

there's also a design principle that any important resource ought to be identifiable by a URI 🧚 So this spec describes how a server can assign URIs to the query itself, or to a specific query result, for use later in a GET request.

here's the summary, bestie πŸ“Š

+==========+=================+=================+===================+
|          |GET              |QUERY            | POST              |
+==========+=================+=================+===================+
|Safe      |yes              |yes              | potentially no    |
+----------+-----------------+-----------------+-------------------+
|Idempotent|yes              |yes              | potentially no    |
+----------+-----------------+-----------------+-------------------+
|URI for   |yes (by          |optional         | no                |
|query     |definition)      |(Location        |                   |
|itself    |                 |response field)  |                   |
+----------+-----------------+-----------------+-------------------+
|URI for   |optional         |optional         | optional          |
|query     |(Content-Location|(Content-Location| (Content-Location |
|result    |response field)  |response field)  | response field)   |
+----------+-----------------+-----------------+-------------------+
|Cacheable |yes              |yes              | yes, but only for |
|          |                 |                 | future GET or     |
|          |                 |                 | HEAD requests     |
+----------+-----------------+-----------------+-------------------+
|Content   |"no defined      |expected         | expected          |
|(body)    |semantics"       |(semantics per   | (semantics per    |
|          |                 |target resource) | target resource)  |
+----------+-----------------+-----------------+-------------------+

Table 1: Summary of Relevant Method Properties

1.1. Terminology πŸ“–

this doc uses terminology defined in Section 3 of [HTTP] πŸ’œ

on top of that, it uses URI query parameter for the parameters in the query component of a URI (Section 4.2.2 of [HTTP]), and query content for the request content (Section 6.4 of [HTTP]) of a QUERY request πŸŽ€

1.2. Notational Conventions πŸ“

the big keywords in this document, "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL", are read the BCP 14 way ([RFC2119] [RFC8174]). That only happens when, and only when, they show up in ALL CAPS, just like shown here ⭐

2. QUERY Method πŸ‘‘

so the QUERY method starts a server-side query πŸ”₯ GET asks for a representation of whatever's at the target URI (Section 7.1 of [HTTP]). QUERY is different: it asks the target resource to run a query operation within that resource's scope.

the content of the request and its media type together define the query 🧩 The origin server works out the scope of the operation from the target resource.

Servers MUST fail the request if the Content-Type request field ([HTTP], Section 8.3) is missing, or if it doesn't match the request content 🚫

like with every HTTP method, the query part of the target URI helps identify which resource is being queried πŸ” Whether and how it directly changes the query result depends on the resource, so that's out of scope for this spec πŸ™ƒ

QUERY requests are safe with regard to the target resource ([HTTP], Section 9.2.1). That means the client doesn't ask for, or expect, any change to the target resource's state πŸ§˜β€β™€οΈ That still lets the server create extra HTTP resources so more info can be retrieved through them (see Sections 2.3 and 2.4) πŸ¦‹

QUERY requests are also idempotent ([HTTP], Section 9.2.2) πŸ” They can be retried or repeated when needed, for example after a connection failure πŸ’–

as per Section 15.3 of [HTTP], a 2xx (Successful) response code means the request was successfully received, understood, and accepted βœ…

and specifically, a 200 (OK) response means the query was successfully processed, and the results of that processing are enclosed as the response content πŸŽ‰βœ¨

2.1. Media Types and Content Negotiation πŸŽ€

okay so a QUERY request's meaning depends on BOTH the request content AND the metadata around it, like the media type ([HTTP], Section 8.3.1) πŸ’… and if the content and metadata don't match, that's a problem. Any problem with requests where content and metadata are inconsistent MUST be rejected with a 4xx (Client Error) response ([HTTP], Section 15.5). πŸ™…β€β™€οΈ

Here's the breakdown of the different ways things can go wrong, plus which status codes are the right ones to use:

  • If a request has no media type info, it's incorrect by definition and needs to fail with a 4xx status code, like 400 (Client Error). πŸ’€
  • If a media type is specified but the resource doesn't support it, a 415 (Unsupported Media Type) is the move. This also covers the case where the media type is known in general, but it doesn't have the semantics a QUERY to that target resource needs. In both cases, the Accept-Query response field (Section 3) can tell the client which media types ARE supported. 🫢
  • If a media type is specified but doesn't match the actual request content, a 400 (Bad Request) can be returned. A server is not allowed to guess a media type from the request content and then override a missing or "erroneous" value (i.e., "content sniffing"). No sniffing, bestie. πŸ™ˆ
  • If the media type is specified and understood, the content matches it, but the query can't be processed because of what the query actually says, the status 422 (Unprocessable Content) can be used. For example, a syntactically correct SQL query that points at a table that doesn't exist. πŸ”₯
  • If the client asks for a specific response media type using the Accept field ([HTTP], Section 12.5.1) and the resource doesn't support it, a status code of 406 (Not Acceptable) is appropriate. πŸ‘‘

2.2. Equivalent Resource πŸ¦‹

The equivalent resource for any QUERY request is a resource that responds to GET requests, represents that QUERY request and its target, and takes both the message content and the metadata into account (Section 6 of [HTTP]). This especially includes representation metadata (Section 8 of [HTTP]), like the content's media type. ✨

So basically, the equivalent resource is derived from the resource that implements QUERY by folding in the request content. 🧚

The term equivalent resource is a handy tool for defining how other HTTP stuff works, like selected representations. Servers can assign URIs to these resources, but they don't have to (see Section 1.1 of [URI]). If they do, those resources become accessible for GET requests. 🌸

2.3. Content-Location Response Field πŸ“

A successful response (2xx, Section 15.3 of [HTTP]) can include a Content-Location header field containing an identifier for a resource that matches the results of the operation. See Section 8.7 of [HTTP] for the details. This is the server basically saying, "hey, you can send a GET request to this URI to pull up the results of the query we just ran." The indicated resource might be temporary, so don't get too attached. πŸ₯Ή

See Appendix A.4.1 for an example. πŸ’–

2.4. Location Response Field 🌈

A server can assign a URI to the equivalent resource (Section 2.2) of a QUERY request. If it does, that URI can go in the Location header field of the 2xx response (see Section 10.2.2 of [HTTP]). This is a claim that a client can send a GET request to that URI to repeat the query without resending the query content. Iconic. πŸ™Œ This resource's URI might be temporary too. If a future request fails, the client can retry using the original QUERY request target and the content it already submitted. πŸ”

See Appendix A.4.2 for an example. ⭐

2.5. Redirection πŸ¦‹

Sometimes the server decides to respond to a QUERY request indirectly by redirecting the user agent to a different URI (see Section 15.4 of [HTTP]). 🧭

A response with status code 301 (Moved Permanently, [HTTP], Section 15.4.2) or 308 (Permanent Redirect, [HTTP], Section 15.4.9) means the target resource has permanently moved to a different URI, which is given in the Location response field ([HTTP], Section 10.2.2). Likewise, a response with status code 302 (Found, [HTTP], Section 15.4.3) or 307 (Temporary Redirect, [HTTP], Section 15.4.8) means the target resource has temporarily moved. In all four cases, the server is suggesting that the user agent can accomplish its original QUERY request by sending a similar QUERY request to the new target URI in the Location. Main character redirect energy. πŸ‘‘

Note that the exceptions for turning a POST into a GET after a 301 or 302 response do NOT apply to QUERY requests. Not the vibe. πŸ™…β€β™€οΈ

A response to QUERY with status code 303 (See Other, Section 15.4.4 of [HTTP]) means the original query can be done with a normal retrieval request on the URI in the Location response field ([HTTP], Section 10.2.2). For HTTP, that means sending a GET request to the new target URI, as shown in the example in Appendix A.4.3. πŸ’…

2.6. Conditional Requests πŸ’œ

The selected representation (Section 3.2 of [HTTP]) of a QUERY request is the same as for a GET request to that QUERY request's equivalent resource (Section 2.2). 🫢

A conditional QUERY requests that the selected representation (i.e., the query results after any content negotiation) be returned in the response only under the circumstances described by the conditional header field(s), as defined in Section 13 of [HTTP]. 🎯

See Appendix A.5 for examples. 🌷

2.7. Caching 🍯

The response to a QUERY method is cacheable, and a cache MAY use it to satisfy subsequent QUERY requests as per Section 4 of [HTTP-CACHING]. πŸ’Ύ

The cache key for a QUERY request (Section 2 of [HTTP-CACHING]) MUST incorporate the request content (Section 6 of [HTTP-CACHING]) and the related metadata (Section 8 of [HTTP]). This part is non-negotiable, periodt. πŸ”’

To make the cache more efficient, caches MAY strip out semantically insignificant differences from the request content and related metadata first. For instance, by:

  • removing content encoding(s) (Section 8.4 of [HTTP]).
  • normalizing based on knowledge of format conventions, as indicated by any media subtype suffix in the request's Content-Type field (e.g., "+json"; see Section 4.2.8 of [RFC6838]).
  • normalizing based on knowledge of the semantics of the content itself, as indicated by the request's Content-Type field.

Note that any transformation like this is done solely to generate a cache key. It does NOT change the request itself. 🧼

Clients can use the no-transform cache directive (Section 5.2.1.6 of [HTTP-CACHING]) to say they want NO such transformation to happen. But heads up, this directive is just advisory. πŸ€·β€β™€οΈ

Caching QUERY responses is way more complex than caching GET responses, because the cache needs to read the request's whole content before it can figure out the cache key. 😭 If a QUERY response includes a Location response field (Section 2.4) pointing to a URI for an equivalent resource (Section 2.2), clients can switch to GET for later requests, which makes processing simpler. Big brain move. 🧠

2.8. Range Requests πŸŽ€

Range Requests for QUERY work exactly the same as they do for GET, as defined in Section 14 of [HTTP]. Byte Range Requests (the only range unit defined at the time of writing), however, don't offer much value for QUERY results. πŸ™ƒ

Query formats often have their own built-in way of limiting results or paging through them, like FETCH FIRST ... ROWS ONLY in SQL. Those built-in features are expected to be used instead of HTTP Range Requests. Use the tools you already have, babe. πŸ§šβ€β™€οΈ

3. The Accept-Query Header Field ✨

The Accept-Query response header field lets a resource directly signal that it supports the QUERY method, and it names the specific query format media type(s) that may be used. πŸ“£

Accept-Query contains a list of media ranges (Section 12.5.1 of [HTTP]) using "Structured Fields" syntax [STRUCTURED-FIELDS]. Media ranges are represented by a List Structured Header Field of either Tokens or Strings, holding the media range value without parameters. 🍰

Media type parameters, if there are any, get mapped to Structured Field Parameters with the String or Token type. Choosing Token versus String is semantically insignificant. Recipients MAY convert Tokens to Strings, but they MUST NOT process them differently based on the type they received. Same vibe, different outfit. πŸ‘—

Media types don't map exactly to Tokens. For instance, they allow a leading digit. In cases like these, the String format needs to be used. πŸ”€

The only supported uses of wildcards are */*, which matches any type, and xxxx/*, which matches any subtype of the indicated type. 🌟

The order of the types listed in the field value is not significant. πŸ™†β€β™€οΈ

The value of the Accept-Query field applies to every URI on the server that shares the same path. In other words, the query component is ignored. If requests to the same resource return different Accept-Query values, the most recently received fresh value (per Section 4.2 of [HTTP-CACHING]) is used. πŸ•°οΈ

For example:

Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"

Even though this field's syntax looks similar to other fields, like Accept (Section 12.5.1 of [HTTP]), it's a Structured Field, so it MUST be processed as specified in Section 4 of [STRUCTURED-FIELDS]. Don't let the lookalike fool you. πŸ•΅οΈβ€β™€οΈπŸ’–

4. Security Considerations πŸ”

The QUERY method is subject to the same general security considerations as all HTTP methods, as described in [HTTP]. Take it seriously, bestie. 😀

QUERY can be used as an alternative to passing request information in the URI (e.g., in the query component). This is preferred in some cases, because the URI is more likely to be logged or otherwise processed by intermediaries than the request content. In other cases, where the query contains sensitive information, the potential for the URI to be logged might be the reason to pick QUERY over GET. πŸ•΅οΈ

If a server creates a temporary resource to represent the results of a QUERY request (e.g., for use in the Location or Content-Location field), assigns a URI to that resource, and the request contains sensitive information that cannot be logged, then that URI SHOULD be chosen such that it does not include any sensitive portions of the original request content. Keep the secrets out of the link, okay?! 🀫

Caches that normalize QUERY content incorrectly, or in ways that are significantly different from how the resource processes the content, can return an incorrect response if the normalization results in a false positive. This one is a real danger zone. πŸ’€

A QUERY request from user agents implementing Cross-Origin Resource Sharing (CORS) will require a "preflight" request, because QUERY does not belong to the set of CORS-safelisted methods (see [FETCH]). Extra step, but it's giving safety. πŸ›‘οΈ

5. IANA Considerations πŸ“‹

5.1. Registration of the QUERY Method πŸŽ‰

IANA has added the QUERY method to the "Hypertext Transfer Protocol (HTTP) Method Registry" at <http://www.iana.org/assignments/http-methods> (see Section 16.3.1 of [HTTP]). Officially in the registry, no cap. πŸ₯³

       +=============+======+============+========================+
       | Method Name | Safe | Idempotent | Specification          |
       +=============+======+============+========================+
       | QUERY       | yes  | yes        | Section 2 of RFC 10008 |
       +-------------+------+------------+------------------------+

                     Table 2: QUERY Method Definition

5.2. Registration of the Accept-Query Field ✨

okay so IANA officially added Accept-Query to the "Hypertext Transfer Protocol (HTTP) Field Name Registry" at <https://www.iana.org/assignments/http-fields> (see Section 16.1.1 of [HTTP]) πŸ“‹πŸŽ€. here's the entry, and yes it's giving official πŸ’…

    +==============+===========+============+==============+==========+
    | Field Name   | Status    | Structured | Reference    | Comments |
    |              |           | Type       |              |          |
    +==============+===========+============+==============+==========+
    | Accept-Query | permanent | List       | Section 3 of |          |
    |              |           |            | RFC 10008    |          |
    +--------------+-----------+------------+--------------+----------+

                   Table 3: Accept-Query Field Definition

6. References πŸ“š

6.1. Normative References

these are the ones the spec leans on hard πŸ€“ you really want these bookmarked

6.2. Informative References

these ones are extra context, the "nice to know" shelf 🌸


Appendix A. Examples πŸŽ€

these examples are just for illustration 🫢 real queries that are this short? honestly just use GET, it's the better fit

the media type in most of them is "application/x-www-form-urlencoded" (the one browser user clients send with POST requests, defined in "application/x-www-form-urlencoded" (https://url.spec.whatwg.org/#application/x-www-form-urlencoded) in [URL]). the Content-Length fields are left out to keep things short πŸ“

A.1. Simple Query πŸ’–

here's a simple query that gets a direct response:

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" },
  { "surname": "Dubois",
    "givenname": "Camille",
    "email": "camille.dubois@example.net" }
]

A.2. Discovery of QUERY Support πŸ”

a simple way to find out if QUERY is supported is the OPTIONS (Section 9.3.7 of [HTTP]) method 🧚

OPTIONS /contacts HTTP/1.1
Host: example.org

Response:

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

the Allow response field (Section 10.2.1 of [HTTP]) shows the set of supported methods on the specified resource πŸ’…

there are other options too. you can just try a QUERY request without knowing anything about server support first πŸ¦‹ the server would then either process the request, or it could respond with a 4xx status such as 405 (Method Not Allowed, Section 15.5.6 of [HTTP]), including the Allow response field.

A.3. Discovery of QUERY Formats 🧁

checking which media types QUERY accepts is possible through the Accept-Query response field (Section 3):

HEAD /contacts HTTP/1.1
Host: example.org

Response:

HTTP/1.1 200 OK
Content-Type: application/xhtml
Accept-Query: application/x-www-form-urlencoded, application/sql

which responses will contain Accept-Query depends on the resource being accessed πŸ‘‘

an alternative to checking Accept-Query is to make a QUERY request and then, if you get a 4xx status such as a 415 response (Unsupported Media Type, Section 15.5.16 of [HTTP]), look at the Accept response field (Section 12.5.1 of [HTTP]) 🌈

HTTP/1.1 415 Unsupported Media Type
Content-Type: application/xhtml
Accept: application/x-www-form-urlencoded, application/sql

A.4. Content-Location, Location, and Indirect Responses 🌸

as described in Sections 2.3 and 2.4, the Content-Location and Location response fields in success responses (2xx, Section 15.3 of [HTTP]) give you a way to find alternate resources that will respond to GET requests. they can hold either the received result of the request, or serve future requests for the same operation ✨ going back to the example from Appendix A.1:

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
Content-Location: /contacts/stored-results/17
Location: /contacts/stored-queries/42
Last-Modified: Sat, 25 Aug 2012 23:34:45 GMT
Date: Sun, 17 Nov 2024, 16:10:24 GMT

[
  { "surname": "Smith",
    "givenname": "John",
    "email": "smith@example.org" },
  { "surname": "Jones",
    "givenname": "Sally",
    "email": "sally.jones@example.com" },
  { "surname": "Dubois",
    "givenname": "Camille",
    "email": "camille.dubois@example.net" }
]

A.4.1. Using Content-Location πŸ¦„

the Content-Location response field from above points to a resource holding the result for the QUERY response it came on πŸ’œ

GET /contacts/stored-results/17 HTTP/1.1
Host: example.org
Accept: application/json

Response:

HTTP/1.1 200 OK
Last-Modified: Sat, 25 Aug 2012 23:34:45 GMT
Date: Sun, 17 Nov 2024, 16:10:25 GMT

[
  { "surname": "Smith",
    "givenname": "John",
    "email": "smith@example.org" },
  { "surname": "Jones",
    "givenname": "Sally",
    "email": "sally.jones@example.com" },
  { "surname": "Dubois",
    "givenname": "Camille",
    "email": "camille.dubois@example.net" }
]

heads up 🚨 there's no guarantee the server keeps this resource around forever, so after an error response, the client would need to redo the original QUERY request to get a new alternative location πŸ’€

A.4.2. Using Location πŸ§šβ€β™€οΈ

the Location response field points to a resource that will respond to GET with a current result for the same process and parameters as the original QUERY request πŸŽ€

GET /contacts/stored-queries/42 HTTP/1.1
Host: example.org
Accept: application/json

in this example, one entry got removed at 2024-11-17T16:12:01Z (as the Last-Modified field shows), so the response only has two entries:

HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Sun, 17 November 2024, 16:12:01 GMT
ETag: "42-1"
Date: Sun, 17 Nov 2024, 16:13:17 GMT

[
  { "surname": "Smith",
    "givenname": "John",
    "email": "smith@example.org" },
  { "surname": "Dubois",
    "givenname": "Camille",
    "email": "camille.dubois@example.net" }
]

assuming the server still exposes the resource and the query result hasn't changed, a later conditional GET request with the following:

If-None-Match: "42-1"

would get a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) πŸ™ˆπŸ«Ά

A.4.3. Indirect Responses πŸ’«

servers can send "indirect" responses (Section 2.5) using the status code 303 (See Other, Section 15.4.4 of [HTTP]) 🌷

given the request at the start of Appendix A.4, a server might respond with:

HTTP/1.1 303 See Other
Content-Type: text/plain
Date: Sun, 17 Nov 2024, 16:13:17 GMT
Location: /contacts/stored-queries/42

See stored query at "/contacts/stored-queries/42".

this is pretty much like putting Location on a direct response, except no result for the query comes back. it lets the server just generate or reuse an alternative resource, and that resource can then be used like in Appendix A.4.2 πŸ“βœ¨

A.5. Conditional Requests ✨

okay so this one is my fave πŸ¦‹ picture a resource that does QUERY and takes application/sql and application/xslt+xml [XSLT] as request media types, and it can hand back text/csv responses. The data set it's querying has RFC document info in it, and the query groups everything by decade πŸ“š

QUERY /rfc-index.xml HTTP/1.1
Host: example.org
Date: Sun, 7 Sep 2025, 00:00:00 GMT
Content-Type: application/xslt+xml
Accept: text/csv

...Query content using XSLT...

Response:

HTTP/1.1 200 OK
Date: Sun, 7 Sep 2025, 00:00:00 GMT
Location: /stored-queries/4815162342
Content-Type: text/csv
Accept-Query: "application/sql", "application/xslt+xml"
Last-Modified: Sun, 31 Aug 2025, 08:44:00 GMT
Vary: Accept-Query, Content-Encoding, Content-Type

decade, total, with errata, % with errata, average page count
1960, 26, 5, 19.2, 5.3
1970, 666, 18, 2.7, 6.1
1980, 376, 44, 11.7, 23.4
1990, 1593, 269, 16.9, 25.5
2000, 2888, 1048, 36.3, 27.3
2010, 2954, 895, 30.3, 26.1
2020, 1133, 230, 20.3, 26.2

Here, the server gave the path /stored-queries/4815162342 to the equivalent resource (Section 2.4), so you can use it later with GET πŸŽ€

Later on, the client repeats the query but says "only send me stuff if it changed" πŸ‘€

QUERY /rfc-index.xml HTTP/1.1
Host: example.org
Date: Mon, 8, Sep 2025, 11:00:00 GMT
Content-Type: application/sql
Accept: text/csv
If-Modified-Since: Sun, 31 Aug 2025, 08:44:00 GMT
Vary: Accept-Query, Content-Type

...Same query, but using SQL...

The data being queried didn't change, so the server replies with πŸ‘‡

HTTP/1.1 304 Not Modified
Date: Mon, 8 Sep 2025, 11:00:00 GMT
Content-Type: text/csv
Location: /stored-queries/4815162342
Accept-Query: "application/sql", "application/xslt+xml"
Last-Modified: Sun, 31 Aug 2025, 08:44:00 GMT
Vary: Accept-Query, Content-Type

Since the server gave a URI for the equivalent resource, that resource can be accessed with GET. The best part is that this skips resending the query request's content all over again πŸ™Œ

GET /stored-queries/4815162342 HTTP/1.1
Host: example.org
Date: Sun, 21, Sep 2025, 12:08:00 GMT
Accept: text/csv
If-Modified-Since: Sun, 31 Aug 2025, 00:00:00 GMT

This time the state of the data set really did change, so new content comes back πŸ’…

HTTP/1.1 200 OK
Date: Sun, 21, Sep 2025, 12:08:00 GMT
Content-Type: text/csv
Last-Modified: Thu, 18 Sep 2025, 19:56:00 GMT
Vary: Accept-Query, Content-Encoding, Content-Type

decade, total, with errata, % with errata, average page count
1960, 26, 5, 19.2, 5.3
1970, 666, 18, 2.7, 6.1
1980, 376, 44, 11.7, 23.4
1990, 1593, 269, 16.9, 25.5
2000, 2888, 1048, 36.3, 27.3
2010, 2954, 895, 30.3, 26.1
2020, 1133, 230, 20.3, 26.2

(Note the change in the row for this decade.) πŸ‘€

The diagrams below show how conditional requests work, and how they can look different when a URI is assigned to the equivalent resource (and the client is taking advantage of it). The made-up field name "Validator" is just for demo purposes 🧚

         Client                             Resource
         |                                         |
         | QUERY with content                      |
         +---------------------------------------->|
         |                                         |
         |                                  200 OK |
         |                          Validator: foo |
         |<----------------------------------------+
         |                                         |
         | QUERY with content                      |
         | (conditional on 'foo')                  |
         +---------------------------------------->|
         |                                         |
         |                        304 Not Modified |
         |                          Validator: foo |
         |<----------------------------------------+
         |                                         |
         |                                  +--------------+
         |                                  | State Change |
         |                                  +--------------+
         |                                         |
         | QUERY with content                      |
         | (conditional on 'foo')                  |
         +---------------------------------------->|
         |                                         |
         |                                  200 OK |
         |                          Validator: bar |
         |<----------------------------------------+
         |                                         |

Figure 1: Data Flow with QUERY Only

Client                   Resource
|                               |
| QUERY with content            |         Equivalent Resource
+------------------------------>|            (generates /xyz)
|                               +---------------------------o
|                               |                           |
|                        200 OK |                           |
|                Validator: foo |                           |
|                Location: /xyz |                           |
|<------------------------------+                           |
|                               |                           |
| GET                                                       |
| (conditional on 'foo')                                    |
+---------------------------------------------------------->|
|                                                           |
|                                          304 Not Modified |
|                                            Validator: foo |
|<----------------------------------------------------------+
|                                                           |
|                                                    +--------------+
|                                                    | State Change |
|                                                    +--------------+
| GET                                                       |
| (conditional on 'foo')                                    |
+---------------------------------------------------------->|
|                                                           |
|                                                    200 OK |
|                                            Validator: bar |
|<----------------------------------------------------------+
|                                                           |

Figure 2: Data Flow with GET to Equivalent Resource


A.6. More Query Formats πŸ¦‹

The examples below show requests on a JSON-shaped [RFC8259] database of RFC errata 🐞

The request below uses eXtensible Stylesheet Language Transformations (XSLT) to pull out errata info summarized per year and the defined errata types ✨

QUERY /errata.json HTTP/1.1
Host: example.org
Content-Type: application/xslt+xml
Accept: application/xml, text/csv

<transform xmlns="http://www.w3.org/1999/XSL/Transform"
  xmlns:j="http://www.w3.org/2005/xpath-functions"
  version="3.0">

  <output method="text"/>

  <param name="input"/>

  <variable name="json"
    select="json-to-xml(unparsed-text($input))"/>

  <variable name="sc">errata_status_code</variable>
  <variable name="sd">submit_date</variable>

  <template match="/">
    <text>year, total, rejected, verified, hdu, reported</text>
    <text>&#10;</text>
    <variable name="en" select="$json//j:map"/>
    <for-each-group select="$en"
      group-by="substring-before(j:string[@key=$sd],'-')">
      <sort select="current-grouping-key()"/>
      <variable name="year" select="current-grouping-key()"/>
      <variable name="errata" select=
        "$en[$year=substring-before(j:string[@key=$sd],'-')]"/>
      <value-of select="concat(
        $year,
        ', ',
        count($errata),
        ', ',
        count($errata['Rejected'=j:string[@key=$sc]]),
        ', ',
        count($errata['Verified'=j:string[@key=$sc]]),
        ', ',
        count(
          $errata['Held for Document Update'=j:string[@key=$sc]]),
        ', ',
        count($errata['Reported'=j:string[@key=$sc]]),
        '&#10;')"/>
    </for-each-group>
  </template>

</transform>

Response:

HTTP/1.1 200 OK
Content-Type: text/csv
Accept-Query: "application/jsonpath", "application/xslt+xml"
Date: Wed, 19 Feb 2025, 17:10:01 GMT

year, total, rejected, verified, hdu, reported
2000, 14, 0, 14, 0, 0
2001, 72, 1, 70, 1, 0
2002, 124, 8, 104, 12, 0
2003, 63, 0, 61, 2, 0
2004, 89, 1, 83, 5, 0
2005, 156, 10, 96, 50, 0
2006, 444, 54, 176, 214, 0
2007, 429, 48, 188, 193, 0
2008, 423, 52, 165, 206, 0
2009, 331, 39, 148, 144, 0
2010, 538, 80, 232, 222, 4
2011, 367, 47, 170, 150, 0
2012, 348, 54, 149, 145, 0
2013, 341, 61, 169, 106, 5
2014, 342, 73, 180, 72, 17
2015, 343, 79, 145, 89, 30
2016, 295, 46, 122, 82, 45
2017, 303, 46, 120, 84, 53
2018, 350, 61, 118, 98, 73
2019, 335, 47, 131, 94, 63
2020, 387, 68, 117, 123, 79
2021, 321, 44, 148, 63, 66
2022, 358, 37, 198, 40, 83
2023, 262, 38, 121, 33, 70
2024, 322, 33, 125, 23, 141
9999, 1, 0, 0, 1, 0

Note the Accept-Query response field saying another query format, JSONPath [RFC9535], is supported too πŸ’œ The request below would report the identifiers of all rejected errata submitted since 2024:

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

$..[
     ?@.errata_status_code=="Rejected"
     && @.submit_date>"2024"
   ]
   ["doc-id"]

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Accept-Query: "application/jsonpath", "application/xslt+xml"
Date: Thu, 20 Feb 2025, 09:55:42 GMT
Last-Modified: Thu, 20 Feb 2025 06:10:01 GMT

[
  "RFC1185","RFC8407","RFC6350","RFC8467","RFC1157","RFC9543",
  "RFC9076","RFC7656","RFC2822","RFC9460","RFC2104","RFC6797",
  "RFC9499","RFC9557","RFC2131","RFC2328","RFC9001","RFC3325",
  "RFC9438","RFC2526","RFC2985","RFC7643","RFC9132","RFC6376",
  "RFC9110","RFC9460","RFC7748","RFC9497","RFC8463","RFC4035",
  "RFC7239","RFC9083","RFC9537","RFC9537","RFC9420","RFC9000",
  "RFC9656","RFC9110","RFC2324","RFC2549","RFC6797","RFC2549",
  "RFC8894"
]

Appendix B. Selection of the Method Name 'QUERY' 🌈

The "Hypertext Transfer Protocol (HTTP) Method Registry" (<http://www.iana.org/assignments/http-methods>) already has three other methods with the properties "safe" and "idempotent": PROPFIND [RFC4918], REPORT [RFC3253], and SEARCH [RFC5323].

It would have been possible to reuse any of these, updating it so it matches what this spec defines as the new method QUERY. Honestly, the early drafts of this spec even used SEARCH πŸ™ˆ

The method name QUERY ultimately won because:

  • The alternatives use a generic media type for the request content (application/xml), and the semantics of the request depend solely on the request content.
  • Furthermore, they all come from the WebDAV activity, which many people have mixed feelings about 😬
  • QUERY captures the relation with the URI's query component really well πŸ’–

Acknowledgements 🫢

okay bestie, the HTTP Working Group deserves a whole parade πŸŽ€ We thank all of them for their ideas, reviews, and feedback, because this spec wouldn't exist without that squad πŸ’–

The following individuals get extra special recognition βœ¨πŸ‘‘: Carsten Bormann, Mark Nottingham, Martin Thomson, Michael Thornburgh, Roberto Polli, Roy Fielding, and Will Hawkins. Literal legends πŸ™Œ


Contributors πŸ¦‹

Ashok Malhotra was part of the early discussions that led to this specification 🌸 Here's how to reach them:

  • Ashok Malhotra
  • Email: malhotrasahib@gmail.com

Discussion on this HTTP method was reopened by AsbjΓΈrn Ulsberg during the HTTP Workshop in 2019 πŸ”₯ Main character energy, honestly πŸ’…


Authors' Addresses πŸ’œ

Here are the people who actually wrote this thing 🧚 Slay, and reach out if you've got questions πŸ’–

Julian Reschke
greenbytes GmbH
Hafenweg 16
48155 MΓΌnster
Germany
Email: julian.reschke@greenbytes.de
URI:   https://greenbytes.de/tech/webdav/
James M Snell
Cloudflare
Email: jasnell@gmail.com
Mike Bishop
Akamai
Email: mbishop@evequefou.be

That's the whole doc, bestie πŸŽ€βœ¨ Thanks for hanging out with me through all five parts πŸ«ΆπŸ’…

πŸ“¦ where this page comes from