For years, many APIs have hidden complex searches behind POST. Not because it made semantic sense, but because it was the practical way out. GET works very well for simple reads, but when a search needs nested filters, arrays, ranges, multiple sorting rules or JSON structures, the URL stops being a comfortable place to put everything. The result became familiar to any backend team: read endpoints with a POST body.
HTTP has now filled that gap with RFC 10008, which defines the QUERY method as an IETF Proposed Standard. Its goal is clear: to allow a query request with a body, like POST, while keeping the properties expected from a read operation: it is safe, idempotent and cacheable.
This does not mean that every framework, proxy, load balancer, CDN and WAF will support it tomorrow without changes. Adoption will take time. But for those designing APIs, maintaining web infrastructure or dealing with advanced search endpoints, QUERY points in a very logical direction: stop using POST as a generic container for complex reads.
The real problem: GET falls short and POST slightly lies
GET is the natural method for requesting data. It is safe, idempotent and cacheable. Its problem appears when the query stops being simple. A URL can carry parameters, but it is not designed to transport large or highly expressive structures. Real-world limits do not depend only on the server, either. The request may pass through browsers, proxies, load balancers, gateways, firewalls, CDNs and observability systems, each with its own restrictions.
There is also a less visible detail: URLs are much more likely to be recorded in logs than request bodies. If sensitive filters are sent in the query string, they may end up in browser history, metrics, traces, referrer headers or intermediate logs.
POST solved the physical part of the problem because it accepts a body. You could send a clean JSON payload with well-structured filters without fighting URL encoding. But the price was semantic loss. For a proxy, a CDN, an HTTP client or a retry system, POST does not natively say “this is a safe read”. It may be a search, but it may also create an order, start a task, submit a form or modify state.
| Method | Safe | Idempotent | Body | Cacheable | Natural use |
|---|---|---|---|---|---|
| GET | Yes | Yes | No defined semantics | Yes | Simple reads |
| QUERY | Yes | Yes | Yes | Yes | Complex queries |
| POST | Not necessarily | Not necessarily | Yes | Conditional | Creation, actions or processes with effects |
In HTTP, “safe” does not mean encrypted, authenticated or protected against attacks. It means that the client is not asking to change the state of the resource. A GET or QUERY may still be logged, consume CPU or update metrics, but it should not create, modify or delete data as part of its main operation.
What QUERY brings
QUERY allows the content of the query to be sent in the request body. That content can be JSON, SQL, JSONPath, forms or another media type supported by the server. The semantics are not defined only by the method, but also by the resource and the Content-Type.
A simple example could look like this:
QUERY /api/servers/search HTTP/1.1
Host: example.com
Content-Type: application/json
Accept: application/json
{
"where": {
"region": ["mad1", "ams1"],
"status": "active",
"ram_gb": { "gte": 128 },
"tags": ["proxmox", "production"]
},
"sort": [
{ "field": "created_at", "direction": "desc" }
],
"limit": 50
}Code language: JavaScript (javascript)
Before QUERY, this usually ended up as something like:
POST /api/servers/search HTTP/1.1
Content-Type: application/jsonCode language: HTTP (http)
It worked, but the method was not telling the whole truth. QUERY does: the server will process a query included in the body and return the result, without the client expecting state changes.
The RFC also introduces the Accept-Query header, which a resource can use to indicate which formats it accepts for queries. For example:
Accept-Query: application/json, application/jsonpath, application/sqlCode language: HTTP (http)
This makes capability discovery possible without relying only on external documentation. A client could first ask with HEAD or OPTIONS, read Accept-Query, and decide whether to send JSON, a form or a query in another format.
Advantages for APIs, CDNs and retries
The first advantage is semantic clarity. An advanced search endpoint no longer has to disguise itself as POST. This helps API design, SDKs, documentation and teams operating infrastructure. The intention is clearer from the HTTP method itself.
The second advantage is idempotency. If a connection drops halfway through a QUERY request, a client or intermediary can retry it without fear of duplicating a purchase, creating two records or launching two jobs. This does not remove the need to design the backend properly, but it gives a much cleaner signal to the HTTP chain.
The third advantage is caching. QUERY is defined as cacheable. That opens an important door for expensive searches that today are executed again and again because they arrive as POST. The RFC requires the cache key to include the request content and related metadata. This is more complex than caching GET, but it is finally defined within the protocol.
| Advantage | Practical impact |
| Structured body | Complex filters without forcing them into the URL |
| Read semantics | Better API design and contracts |
| Idempotency | Safer retries after failures |
| HTTP caching | Possible reuse by proxies and CDNs |
| Less exposure in URLs | Fewer sensitive values in URI logs |
| Discovery with Accept-Query | The server can announce supported formats |
| Content-Location and Location | Results or queries can become GET resources |
One interesting possibility is for the server to answer a QUERY request with Location or Content-Location. This can assign a URI to a stored query or its result, allowing the client to use GET afterwards. It is useful for heavy reports, recurring searches or queries that are worth materialising.
Disadvantages and adoption problems
QUERY is not magic. The first problem is real-world support. Many servers, proxies, CDNs, WAFs, libraries, middleware and monitoring tools are used to GET, POST, PUT, PATCH and DELETE. A new method may be blocked, logged incorrectly, misclassified in metrics or treated as suspicious traffic.
The second problem is caching. Yes, QUERY is cacheable, but caching it properly requires infrastructure that understands the body. Looking only at the URL is not enough. Two JSON bodies with the same meaning but different key order or whitespace could generate different cache keys if they are not normalised. The RFC accounts for this complexity, but bringing it to production will require serious support from CDNs and proxies.
The third point is CORS. In browsers, QUERY is not one of the “simple” CORS methods, which are limited to GET, HEAD and POST. Cross-origin calls will require a preflight request with OPTIONS, and APIs will need to respond with Access-Control-Allow-Methods: QUERY where appropriate.
| Risk or limitation | What to review |
| Server support | Nginx, Apache, Envoy, HAProxy, Node, Go, Java, .NET |
| CDNs and proxies | Body-based caching, keys, normalisation and purging |
| WAFs | Rules that block unknown methods |
| Observability | Logs, metrics, traces and dashboards |
| CORS | Preflight and Access-Control-Allow-Methods |
| SDKs | Clients generated from OpenAPI and other tools |
| Backward compatibility | Temporary POST fallback |
| Security | Size limits, validation and cost control |
It is also worth remembering something basic: putting filters in the body does not make them secret. It is better than exposing them in the URL in some cases, but they still travel to the server and may end up in application logs, traces, APM systems or dumps if logging is not controlled. TLS, logging policies and data minimisation remain mandatory.
When to use GET, QUERY or POST
The practical rule can be quite simple. GET remains the best option for simple, stable reads that can be naturally represented in a URL. If a search fits cleanly in a query string, there is no need to complicate it.
QUERY fits when the operation is a read, but the input needs structure. This includes advanced search, inventory filters, analytics, reports, log queries, vector search, read-only GraphQL or reporting endpoints with many parameters.
POST should remain for what truly implies action: creating resources, submitting forms, launching processes, executing commands, changing state, starting flows or performing operations that are not safe. It can also remain the fallback for years while the ecosystem adopts QUERY.
| Case | Best method |
/users?status=active&page=2 | GET |
| Searching servers by tags, region, RAM and date | QUERY |
| Analytics query with nested filters | QUERY |
| Read-only GraphQL query | QUERY, when supported |
| Creating a user | POST |
| Launching an on-demand backup | POST |
| Generating a report that modifies state or consumes a non-repeatable quota | POST |
| Updating a full resource | PUT |
| Partially modifying a resource | PATCH |
For system administrators, the initial recommendation is not to enable QUERY in production blindly. The sensible approach is to test it first in controlled environments: internal APIs, staging, a specific gateway or new endpoints where proxy, WAF, cache, APM and client behaviour can be measured.
A minimal guide to getting started
A cautious design could follow these steps:
| Step | Recommendation |
| 1 | Identify POST endpoints that are actually reads |
| 2 | Separate safe operations from actions with effects |
| 3 | Define a strict Content-Type, such as application/json |
| 4 | Publish Accept-Query on resources that support it |
| 5 | Apply body size and complexity limits |
| 6 | Normalise queries if they are going to be cached |
| 7 | Configure CORS, WAF, logs and metrics |
| 8 | Keep POST fallback while clients migrate |
A well-designed response could include caching and discovery headers:
HTTP/1.1 200 OK
Content-Type: application/json
Accept-Query: application/json
Cache-Control: public, max-age=60
Content-Location: /api/servers/search-results/8f7a21
Vary: Accept, Content-TypeCode language: HTTP (http)
The delicate part will be the cache key. If QUERY is cached in a CDN, the key must take into account the body, the Content-Type and any header that changes the response. Otherwise, there is a risk of serving incorrect results. For searches with user permissions, you will also need private cache, variation by identity or no caching at all.
The change will be slow, but necessary
QUERY will not replace GET or POST. It organises them. GET will remain perfect for simple reads and shareable URLs. POST will remain the natural method for actions and resource creation. QUERY occupies the space that had been empty for years: safe queries with a body.
Its adoption will depend less on the RFC and more on infrastructure. When Cloudflare, AWS, Fastly, Nginx, Envoy, HAProxy, backend frameworks, HTTP clients and OpenAPI tools start treating QUERY as a first-class citizen, the pattern will enter real architectures. Until then, teams will need to live with POST for complex reads and design migrations calmly.
The update matters because HTTP does not change every day. And when it changes to correct a practice that millions of APIs had normalised, it is worth paying attention.
For years, putting complex filters inside POST was a practical solution. Now it is starting to look like semantic debt with a way out.
Frequently asked questions
Does QUERY replace GET?
No. GET remains the best option for simple, cacheable reads that can be represented in a URL. QUERY is designed for read queries that need a body.
Does QUERY replace POST?
No. POST remains the right method for creating resources, launching actions or running operations with effects. QUERY is for safe and idempotent queries.
Can I use QUERY in production now?
It depends on your stack. You first need to validate support across servers, proxies, CDNs, WAFs, HTTP clients, observability and CORS. For many environments, internal pilots will be the best starting point.
Is QUERY more secure because it does not put filters in the URL?
Not in the cybersecurity sense. It reduces accidental exposure through URLs and intermediate logs, but the body can still be logged by applications or tracing systems. TLS, logging control and validation remain necessary.
