HTTP QUERY Method (RFC 10008): GET With a Body
The HTTP QUERY method (RFC 10008) is a safe, cacheable request with a body. See why it took 16 years, code in 5 languages, tools, pros, cons and rivals.
The HTTP QUERY method is a new request method, standardized as RFC 10008 in June 2026, that lets you send a request body to read data without changing anything. It is safe (read-only), idempotent (repeat it freely) and cacheable, like GET, but it carries its filter in the body, like POST. Use it for complex searches and filters that are too big, too nested or too sensitive for a URL.
It is the first new standard HTTP method since PATCH in 2010, and it fixes a problem every REST API team has hit: GET /search?filter=... hits URL length limits, and POST /search throws away caching and safe retries. This guide covers why QUERY took so long, how to use it, working code, speed, tooling, pros and cons, and the alternatives. If you are designing an API from scratch, pair it with our API design best practices.
Key takeaways
- QUERY = safe + idempotent + cacheable + request body. It sits between GET and POST.
- It is a standard now. RFC 10008, authored by Julian Reschke, James Snell (Cloudflare) and Mike Bishop (Akamai), published June 2026.
- No major browser sends it natively yet. It fits server-to-server and controlled clients today; browser use needs preflight and wrappers.
- It is not faster by itself. The gains come from caching, safe retries and avoiding URL limits, and only if your cache layer supports it.
- Adoption will take years. The RFC itself positions it as an optional addition to GET and POST.
What is the HTTP QUERY method?
QUERY is a request method whose body defines the query. You send a Content-Type that says how to read the body (JSON, form data, SQL, GraphQL, anything with query semantics) and the server returns the matching data.
httpQUERY /products HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
{
"category": "books",
"price": { "max": 30 },
"sort": [{ "field": "rating", "dir": "desc" }],
"page": { "size": 20 }
}
Three properties define it:
- Safe. The request must not change server state, so clients, proxies and crawlers can send it without fear.
- Idempotent. Sending it twice is the same as sending it once, so retries after a timeout are fine.
- Cacheable. A response can be stored, as long as the cache key includes the request body and not just the URL.
Servers can advertise support with the new Accept-Query response header, which lists the body formats an endpoint understands.
Why QUERY exists: the GET vs POST problem
REST APIs have always had a gap. Our REST vs GraphQL vs gRPC guide touches on it; here is the short version.
| GET | POST | QUERY | |
|---|---|---|---|
| Request body | Not meaningful | Yes | Yes |
| Safe (read-only) | Yes | No | Yes |
| Idempotent | Yes | No | Yes |
| Cacheable | Yes | Rarely, by hand | Yes, with body in the key |
| Safe to auto-retry | Yes | No | Yes |
| Sensitive data kept out of URL and logs | No | Yes | Yes |
| Browser support | Native | Native | None native yet |
GET is correct for reads but limited to the URL. Real limits vary between clients, proxies and CDNs, nested filters are awkward to encode, and everything you put in a URL ends up in logs and browser history.
POST accepts any body, so teams use POST /search and GraphQL over POST. It works, but infrastructure now treats your read as a possible write: no automatic retries, no standard caching.
QUERY gives reads a body without giving up the read semantics.
Why did it take so long to release a new HTTP method?
The last new method, PATCH, landed in 2010. QUERY needed roughly 16 years of effort before it reached RFC status. Discussion of the idea runs back to at least the 2019 HTTP Workshop, and the draft went through many revisions and multiple review rounds before RFC 10008 in June 2026. Several things slowed it down:
- The idea was old, the name was not settled. WebDAV already had a SEARCH method (RFC 5323, 2003). The authors deliberately chose a new, general name rather than inherit WebDAV's constraints.
- Caching with a body is hard. A cache must put the request content in the key, and two bodies that mean the same thing but are formatted differently can miss each other. Getting normalization rules that are safe against cache poisoning took care.
- A new method is a deployment problem. Proxies, CDNs, WAFs, frameworks and HTTP libraries all have allow-lists of methods. A method is only useful once enough of that chain lets it through, so standards authors move cautiously.
- It is deliberately modest. QUERY does not replace GET or POST. Its authors framed it as an optional addition, which is also why the semantics depend on the body's content type instead of one fixed query language.
- Redirect and error behavior had to be spelled out. QUERY keeps its method across 301, 302, 307 and 308 redirects. A 303 turns the follow-up into a GET, which lets a server give a query result its own URL.
The slow pace is a feature of how the web is governed: the web runs on installed software that cannot be updated overnight, so the bar for a new method is high.
How to use QUERY: the rules
- Always send
Content-Type. The server cannot guess how to read the body. - Do not change state. If the request has side effects, use POST.
- Respond with normal codes. 400 for a missing or inconsistent content type, 415 for an unsupported format, 422 for a body that is well-formed but cannot be processed, 406 if you cannot produce an acceptable response format.
- Give results a URL when useful. Return
Content-LocationorLocationto point at a stored result, and use 303 See Other to hand back a GET-able resource. - Advertise support. Send
Accept-Query: "application/json"so clients know the endpoint takes it. - Conditional and range requests work like they do for GET (
If-None-Match,If-Modified-Since).
Code examples
These show the shape in each stack. Support depends on your library version, so test it in your environment first.
curl
bashcurl -X QUERY https://api.example.com/products \
-H "Content-Type: application/json" \
-d '{"category":"books","price":{"max":30}}'
JavaScript (fetch)
jsconst res = await fetch("https://api.example.com/products", {
method: "QUERY", // must be uppercase
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ category: "books", price: { max: 30 } }),
});
const data = await res.json();
From a browser page on another origin, QUERY is not CORS-safelisted, so the browser sends an OPTIONS preflight first. Your server must answer it and list QUERY in Access-Control-Allow-Methods.
Node.js server (no framework)
jsimport http from "node:http";
http.createServer(async (req, res) => {
if (req.method === "QUERY" && req.url === "/products") {
let raw = "";
for await (const chunk of req) raw += chunk;
const filter = JSON.parse(raw); // validate this in real code
res.setHeader("Accept-Query", '"application/json"');
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(await searchProducts(filter)));
return;
}
res.statusCode = 404;
res.end();
}).listen(3000);
Python (FastAPI / Starlette)
pythonfrom fastapi import FastAPI, Request
app = FastAPI()
@app.api_route("/products", methods=["QUERY"])
async def search_products(request: Request):
filter = await request.json() # validate with a Pydantic model in real code
return {"items": await search(filter)}
Go
gobody := strings.NewReader(`{"category":"books","price":{"max":30}}`)
req, _ := http.NewRequest("QUERY", "https://api.example.com/products", body)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
C# (.NET 10)
csharp// Client
using var request = new HttpRequestMessage(HttpMethod.Query, "https://api.example.com/products")
{
Content = JsonContent.Create(new { category = "books", maxPrice = 30 })
};
using var response = await httpClient.SendAsync(request);
// Server (minimal API)
app.MapMethods("/products", new[] { HttpMethods.Query }, async (HttpContext ctx) =>
{
var filter = await ctx.Request.ReadFromJsonAsync<ProductFilter>();
return filter is null ? Results.BadRequest() : Results.Ok(await catalog.SearchAsync(filter));
});
.NET 10 ships HttpMethod.Query and HttpMethods.Query, but no dedicated [HttpQuery] attribute, so you map it with MapMethods.
Is QUERY faster? Speed, caching and cost
Not by itself. QUERY does not make a single request quicker than the same POST. No benchmark in the sources we reviewed claims it does, and we will not invent one. What it changes is what the surrounding system can do:
- Cache hits. A shared cache or CDN that understands QUERY can answer repeated identical queries without reaching your server. For popular filters, that is the big win. If your cache layer ignores QUERY, you get no caching at all.
- Safe retries. Because QUERY is idempotent, HTTP clients and service meshes can retry on timeouts automatically, which improves reliability under load. Retrying a
POST /searchis a risk most libraries refuse to take. - No URL limit failures. Large filters stop failing with 414 or silent truncation at some proxy.
- Smaller logs. Sensitive filter values stay out of URLs, access logs and browser history.
The costs run the other way: the cache has to hash or normalize the body to build a key, and browser calls to another origin add a preflight round trip.
Tools and support
Adoption is uneven, so check each link in your own chain before you ship.
| Layer | Status |
|---|---|
| curl | Works with -X QUERY |
Node.js http |
Supports the method (added during the draft phase) |
Go net/http |
Accepts any method string through http.NewRequest |
| .NET 10 | HttpMethod.Query, HttpMethods.Query and IsQuery() |
Rust http crate |
Implementation merged |
| Axum, Quarkus, Bruno | Support in progress or tracked |
| Ruby on Rails | Action Pack rejects QUERY unless configured; discussion opened after publication |
| Browsers | No native API treats QUERY as a first-class method; fetch with a custom method string is the route |
| OpenAPI 3.1 | Does not model QUERY, so endpoints drop out of generated docs |
| CDNs, WAFs, reverse proxies | Uneven; verify body handling and caching |
Pros and cons
Pros
- Correct semantics for reads with a body: safe, idempotent, cacheable.
- No URL length or encoding pain for complex filters.
- Safer retries in clients and gateways.
- Privacy: filters stay out of URLs and most logs.
- Content-type driven, so it works with JSON, forms, GraphQL or SQL-style bodies.
- Standard infrastructure still applies: ETags, conditional requests, redirects and content negotiation.
Cons
- Young ecosystem. Browsers, proxies, WAFs, API gateways and docs tools lag the spec.
- Cache key complexity. Body normalization is easy to get wrong, and a bad cache key can poison a shared cache.
- Security blind spots. Some WAFs drop bodies on unknown methods, and CSRF middleware that skips "safe" methods may skip QUERY endpoints. Review both.
- Browser friction. QUERY triggers a CORS preflight and has no native form or link support.
- Tooling gaps. OpenAPI 3.1 cannot describe it, and some frameworks reject it by default.
- Two ways to do one thing in the meantime, so you may need a POST fallback.
QUERY vs the alternatives (competitors)
| Option | Body | Cacheable | Safe/idempotent | Works everywhere today | Best for |
|---|---|---|---|---|---|
| GET + query string | No | Yes | Yes | Yes | Simple filters, public cacheable reads |
| POST /search | Yes | Rarely | No | Yes | Complex search when you need it to just work |
| GraphQL over POST | Yes | Via persisted queries | Not by HTTP rules | Yes | Flexible client-driven data fetching |
POST then GET (create a search resource, 303 to a GET) |
Yes, then no | Yes | Mixed | Yes | Big queries you want to reuse and share |
| WebDAV SEARCH | Yes | Limited | Yes | Rare | Existing WebDAV systems |
| GET with a body | Undefined | Unreliable | Undefined | No | Nothing: many proxies drop or reject it |
| QUERY | Yes | Yes (body in key) | Yes | Not yet everywhere | Server-to-server and controlled clients |
Our take. If your clients are services you control, QUERY is worth adopting now behind a feature flag with a POST fallback. If you serve the public through browsers and a CDN you do not control, keep GET and POST for now and design endpoints so adding QUERY later is a small change. See also our guides on caching strategies and REST vs GraphQL vs gRPC.
How to adopt QUERY safely: a checklist
- Pick one read-heavy endpoint that already uses
POST /search. - Add a QUERY route next to it, sharing the same handler, and send
Accept-Query. - Confirm every hop (CDN, load balancer, WAF, gateway) forwards QUERY with its body.
- Test cache behavior with two identical and two reordered bodies, and decide how you normalize.
- Check security middleware: WAF rules, CSRF checks and rate limits must treat QUERY as a real method.
- Handle CORS if browsers call it: answer the preflight.
- Keep the POST route until you have measured real traffic.
Build APIs that are ready for what's next
Designing a search-heavy API, or moving an existing one off POST /search? We build and review REST and GraphQL backends for production traffic. Get a scope estimate for your API project, or see how we approach Next.js development and AI agent development, where read-heavy query endpoints are everywhere.
FAQ
What is the HTTP QUERY method?
QUERY is an HTTP request method defined in RFC 10008 that sends a query in the request body while remaining safe, idempotent and cacheable. It is meant for complex read-only requests that do not fit in a URL.
Is QUERY a replacement for GET or POST?
No. GET stays the right choice for simple, URL-addressable reads, and POST stays right for actions that change state. QUERY fills the gap between them for reads that need a body.
Is the HTTP QUERY method supported by browsers?
Not natively as of the RFC's publication. You can send it with fetch using a custom method string, but cross-origin calls need a CORS preflight, and there is no support in forms or links.
Is QUERY cacheable?
Yes, but the cache key must include the request body as well as the URL. If your CDN or proxy ignores the body when caching, you will not get safe caching, so test your stack.
Which frameworks support the QUERY method?
.NET 10, Node.js, Go and the Rust http crate can send or receive it, with Axum, Quarkus and Bruno support in progress, and Rails rejecting it by default. Check your framework's current release notes.
Can I use GET with a body instead?
You should not. HTTP gives a body on GET no defined meaning, and many proxies and servers drop or reject it. QUERY is the standard way to do what people were trying with GET plus a body.
Sources
- InfoQ: IETF Publishes RFC 10008, Adding the QUERY Method for Safe Requests with a Body
- Daniele Teti: HTTP QUERY, the New Method Between GET and POST (RFC 10008)
- Hive Security: RFC 10008 and the Attack Surface Still Catching Up
- vensas: The New HTTP QUERY Method, Use It Today with .NET 10
- IETF datatracker: The HTTP QUERY Method (draft-ietf-httpbis-safe-method-w-body)