← Back to list

HTTP QUERY Method: A New Standard for Complex Reads

The HTTP QUERY method is an official IETF standard (RFC 10008, June 2026) introduced to fill the long-standing gap between GET…

Yash Jain in AlgoMart · 2026-07-11 04:31 · 43 claps · 8.9 min read paywalled
#http-query-method #backend-development #software-development #software-engineering #programming
Open on Medium ↗
Wiki topics: INV · Investing & Markets 💻 · Programming 🌐 · Web Development

HTTP QUERY Method: A New Standard for Complex Reads

Blog Thumbnail

Blog Thumbnail

The HTTP QUERY method is an official IETF standard (RFC 10008, June 2026) introduced to fill the long-standing gap between GET (safe/idempotent, no body) and POST (body, but non-idempotent). It allows complex, read-only requests by carrying a request body (e.g. JSON or SQL) while remaining safe, idempotent and cacheable. The IETF approved QUERY as a Proposed Standard in Nov 2025, and it was published June 2026. The new Accept-Query response header advertises supported query formats. In practice today, support is limited – clients typically feature-detect QUERY (e.g. via an OPTIONS check) and fall back to POST if needed. We compare QUERY to existing methods, show usage examples, discuss security/CORS implications, and give implementation advice with sample code.

History and Standardization

The idea of a “query” method dates back over a decade. In 2015, James Snell proposed reusing WebDAV’s SEARCH method for general queries (WebDAV RFC 5323). However, reusing SEARCH conflicted with existing WebDAV clients. By 2021, the IETF HTTPbis working group adopted the draft under a new name: QUERY. After extensive review (including W3C/WHATWG feedback and an HTTP directorate review), the IETF approved the latest draft on Nov 20, 2025, and the specification was published as RFC 10008 in June 2026.

HTTP Query Method Timeline

HTTP Query Method Timeline

The RFC 10008 itself defines QUERY on the Internet Standards track (Proposed Standard). The RFC’s abstract summarizes its intent: “A QUERY requests that the target process the enclosed content in a safe and idempotent manner and respond with the result”. In short, QUERY is explicitly a read-only method (with side-effect safety guarantees) that carries a body.

Semantics and Usage

The QUERY method is conceptually like GET (a read) but allows a body like POST. Key points from RFC 10008:

  • Safe and Idempotent: By definition, QUERY is safe (does not alter the target resource) and idempotent (repeating the same request has no additional effect). This enables retries and caching. Table 1 below (from RFC 10008) highlights this comparison for GET, QUERY and POST:

Http Method Use

Http Method Use

  • *POST and PATCH responses can be cached only if they include explicit freshness information.
  • Unlike POST, which is not safe or idempotent by default, QUERY guarantees the safe/idempotent semantics of GET while supporting a request body. Thus QUERY brings back caching and retry behavior lost when using POST for queries: proxies and CDNs can safely cache identical QUERY responses (taking the body into account).
  • URL vs. Body: With a QUERY request, the query parameters or filter logic are sent in the request body, not the URL. This avoids long or encoded query strings and leaking sensitive data into URIs. For example, instead of GET /search?role=admin&active=true, one could do:
    QUERY /search HTTP/1.1
    Host: example.com
    Content-Type: application/json

    { "role": "admin", "active": true }
  • The RFC notes that encoding complex queries in the URL is often problematic (length limits, logging of URIs, awkward data structures), and using GET with a body is unreliable (many servers ignore or forbid it). QUERY fixes this by being designed to carry a body.
  • Content Negotiation and Formats: The actual format of the query is determined by content negotiation: the Content-Type of the QUERY request signals how to interpret the body (e.g. JSON, SQL, GraphQL, etc.). Servers may only support certain query types. To help with this, the standard introduces an **Accept-Query** response header. A server may include Accept-Query: <media-types> in its responses to indicate which query formats it supports. (E.g., Accept-Query: application/json, application/sql.) Clients can HEAD or OPTIONS the resource to read Accept-Query and choose an appropriate format. If a client sends an unsupported media type, the server should reply 415 Unsupported Media Type and include an Accept or Accept-Query header with valid types.
  • Redirection and Resource URIs: QUERY can either return results directly (2xx) or indirectly. For large queries, a server may respond 303 See Other with a Location header pointing to a stored query resource. Similarly, a response may include Content-Location or Location headers for result caching or repeatable GETs. In all cases, the request semantics stay safe/idempotent; the difference is only where and how the results are returned (directly or via a follow-up GET).

Example Requests/Responses

Below are illustrative examples (based on RFC 10008) showing how QUERY is used:

  • Simple Query with JSON (direct 200 response):
  QUERY /contacts HTTP/1.1
  Host: example.org
  Content-Type: application/json
  Accept: application/json

  {"q": "Smith", "limit": 5}
  • Response:
  HTTP/1.1 200 OK
  Content-Type: application/json

  [
    {"surname": "Smith", "givenname": "John", "email": "jsmith@example.org"},
    {"surname": "Smith", "givenname": "Jane", "email": "jsmith@example.com"}
  ]
  • Discovering QUERY Support (via OPTIONS):
  OPTIONS /contacts HTTP/1.1
  Host: example.org
  • Response:
  HTTP/1.1 200 OK
  Allow: GET, QUERY, OPTIONS, HEAD
  • The Allow header lists the supported methods, including QUERY. If QUERY were not supported, the server could return 405 Method Not Allowed instead.
  • Advertising Query Formats (via Accept-Query):
 HEAD /contacts HTTP/1.1
 Host: example.org
  • Response:
  HTTP/1.1 200 OK
  Accept-Query: application/json, application/sql
  • This indicates the server accepts either JSON-formatted queries or SQL queries.
  • Indirect Query (303 Redirect):
  QUERY /contacts HTTP/1.1
  Host: example.org
  Content-Type: application/sql
  Accept: text/csv

  SELECT surname, email WHERE active=true
  • Response (no immediate results, see other URI):
  HTTP/1.1 303 See Other
  Location: /contacts/query-12345
  • The client can then GET /contacts/query-12345 to retrieve the cached results.

These examples show how QUERY carries a body and uses standard headers. Unlike POST, there is no automatic side-effect; these are treated as read operations. Because QUERY is cacheable, identical queries can be cached by intermediaries, subject to normal cache rules (e.g. Vary on body).

Method Comparison

The table below summarizes key attributes of various HTTP methods (safe/idempotent/cacheable) and their typical roles. (This is adapted from MDN and RFC 10008 data.)

Method Comparison

Method Comparison

(*POST/PATCH responses are cacheable only if explicit freshness and Content-Location headers are provided.) Importantly, only GET, HEAD, OPTIONS, and now QUERY are safe/idempotent. This means QUERY can leverage caching and automatic retry safely, unlike POST.

Security and CORS Considerations

Because QUERY is a new method, there are a few implications to note:

  • Sensitive Data in URI: One motivation for QUERY is to avoid putting sensitive query parameters in the URL. If a server generates a new URI for query results (via Location or Content-Location), it should not encode any sensitive request data into that URI. In other words, temporary result URLs should not expose private filters or credentials.
  • Cache Normalization: Caches must be careful. If a cache normalizes query bodies (for deduplication), it must do so exactly as the server would process them. Any incorrect normalization could yield wrong cached results. In practice, most caches will key on the entire request (method + URI + body).
  • Cross-Origin (CORS) Preflight: In browsers, QUERY is not a CORS-safelisted method (the safelisted methods are GET, HEAD, POST). This means any cross-origin QUERY request will trigger a CORS preflight (OPTIONS) check. Developers should be aware: using QUERY from web frontends requires proper CORS setup and may incur the overhead of preflight.

Implementation and Adoption

Because QUERY is brand-new, support in clients, frameworks and servers is still emerging:

  • Clients and Browsers: Modern HTTP libraries (like fetch, axios, Python’s requests, etc.) typically allow sending a custom method string. For example, Python’s requests can do requests.request("QUERY", url, json={...}). Node.js fetch also accepts any method token. However, older tools or frameworks might restrict methods to the common ones. For example, many browsers or API gateways simply ignore or strip bodies on GET requests, so trying to workaround by sending a body with GET is unreliable. As per the RFC, feature-detecting QUERY is recommended: one can issue an OPTIONS or try a harmless QUERY and see if one gets a 200 or a 405 Method Not Allowed. If 405 or not supported, fall back to POST.
  • Servers and Proxies: Many HTTP servers (nginx, Apache, IIS, etc.) and proxies (Cloudflare, AWS ALB/CloudFront, etc.) do not yet recognize QUERY by default. Configurations may be needed to allow it (for example, adding QUERY to allowed methods). If a server doesn’t support it, it will typically respond 405 or may list only standard methods in Allow. The IETF draft expected such roll-out delays: one comment notes CloudFront already blocks GET with body, so supporting QUERY will require updates to intermediaries. In short, today you should assume limited support. Frameworks and servers will need to catch up; in the meantime, developers should write fallback logic. For example, if a QUERY request fails or is refused, retry as POST to the same endpoint (the semantics will match, since a POST with the same body was how many APIs did complex reads before).
  • Discovery: To handle varied support, clients can use HTTP options:
  OPTIONS /search HTTP/1.1
  Host: example.com
  • A compliant server will list QUERY in Allow if supported. Alternatively, one can simply send QUERY and check if the response is 405 (with an Allow header) or success.

Best Practices and Migration

  • Use QUERY for Complex Read APIs: The primary use case is APIs that perform filtered, sorted, paginated, or graph-like queries where the parameters exceed a URL’s convenience. If your current design uses POST /search purely as a read (no side-effects), consider switching to QUERY for semantic clarity and to regain caching/retry benefits. Examples include faceted search, reporting queries, or any endpoint that takes a complex JSON filter.
  • Fallback to POST: For backward compatibility, detect if QUERY is supported and otherwise send the same payload via POST (keeping the same URL). This way, you get the safety of POST fallback where needed. A sequence flow might look like:
  sequenceDiagram
    participant C as Client
    participant S as Server
    C->>S: QUERY /search with {body}
    alt Server supports QUERY
      S-->>C: HTTP/200 OK (result data)
    else
      S-->>C: HTTP/405 Method Not Allowed
      C->>S: POST /search with same {body}
      S-->>C: HTTP/200 OK (result data)
    end
  • Keep Bookmarkable Queries on GET: If your queries produce a shareable or bookmarkable link (i.e. all query parameters fit in a URL), continue to use GET. Remember, QUERY-based requests with bodies cannot be bookmarked or retried by manually copying URLs.
  • Cache Keys Include Body: If you implement caching (client-side or on proxies), be sure to include the request body in the cache key. A QUERY request with the same URL but different body is a distinct query. RFC 10008 explicitly notes that caching systems must incorporate the full content when caching a QUERY response.
  • Content-Location and Location: If your server generates URIs for queries or results, use the Content-Location header for a URI that can return the same result, and Location for a URI that, when GETed, re-executes or retrieves the query. This lets clients use plain GET thereafter if needed.

Code Examples

Here are some sample code snippets showing how to use QUERY in practice:

  • cURL (shell):
  curl -X QUERY "https://api.example.com/products" \
       -H "Content-Type: application/json" \
       -d '{"category":"laptop","filters":{"price":{"min":500,"max":1500}}}'
  • This sends a QUERY request with a JSON body.
  • JavaScript (Fetch API, e.g. in Node or browser):
  const res = await fetch("https://api.example.com/products", {
    method: "QUERY",
    headers: {
      "Content-Type": "application/json",
      "Accept": "application/json"
    },
    body: JSON.stringify({
      category: "laptop",
      filters: { price: { min: 500, max: 1500 } }
    })
  });
  const data = await res.json();
  console.log(data);
  • Node.js (Express server):
  const express = require('express');
  const app = express();
  app.use(express.json());

  // Express does not have built-in `app.query()`, but you can catch it:
  app.all('/search', (req, res, next) => {
    if (req.method === 'QUERY') {
      // Handle query logic, reading req.body
      const query = req.body;
      // ... perform database search or filter ...
      res.json(results);
    } else {
      next();
    }
  });const express = require('express'); const app = express(); app.use(express.json());  // Express does not have built-in `app.query()`, but you can catch it: app.all('/search', (req, res, next) => {   if (req.method === 'QUERY') {     // Handle query logic, reading req.body     const query = req.body;     // ... perform database search or filter ...     res.json(results);   } else {     next();   } });
  • Python (requests client):
  import requests
  url = "https://api.example.com/data"
  body = {"filter": {"type": "active"}}
  response = requests.request("QUERY", url, json=body)
  print(response.status_code, response.json())
  • Python (Flask server):
  from flask import Flask, request, jsonify
  app = Flask(__name__)

  # Flask allows custom methods in routes
  @app.route('/data', methods=['GET','QUERY'])
  def data():
      if request.method == 'QUERY':
          query = request.get_json()
          # ... perform query ...
          return jsonify(results)
      else:
          # handle normal GET if needed
          return jsonify({"message": "Please use QUERY for complex queries."})

Each example above treats QUERY much like a POST with a JSON body, but the semantics are read-only.

Summary

The new HTTP QUERY method is now an official standard for sending complex queries. It is safe, idempotent, and cacheable by definition. It allows APIs to use request bodies for rich filtering without misusing POST. Adoption will take time – until then, clients should detect support (e.g. via OPTIONS) and gracefully fall back to POST. When supported, QUERY gives API designers a semantically correct way to do large read-only queries, recovering caching and retry benefits for those operations. The full specification and examples are in [RFC 10008][28] and the IETF announcements.


메타데이터
post_id
9e7587cd6bec
slug
http-query-method-a-new-standard-for-complex-reads-9e7587cd6bec
url
https://medium.com/algomart/http-query-method-a-new-standard-for-complex-reads-9e7587cd6bec
canonical_url
https://medium.com/algomart/http-query-method-a-new-standard-for-complex-reads-9e7587cd6bec
author_url
https://medium.com/@yashjainio
status
ok
fetched_at
2026-07-13 06:23:13