DEV Community

Cover image for HTTP QUERY Is Official: Should You Replace POST /search Yet?
Techifive for Techifive

Posted on

HTTP QUERY Is Official: Should You Replace POST /search Yet?

HTTP QUERY Is Official: Should You Replace POST /search Yet?

For years, API developers have written endpoints like this:

POST /products/search HTTP/1.1
Content-Type: application/json

{
  "filters": {
    "category": ["laptops", "tablets"],
    "price": { "min": 500, "max": 2000 },
    "inStock": true
  },
  "sort": [
    { "field": "rating", "direction": "desc" }
  ],
  "page": { "size": 50 }
}
Enter fullscreen mode Exit fullscreen mode

Nothing is being created. Nothing is being updated.

The client is asking a complicated question, but GET becomes awkward once the input turns into nested filters, ranges, sorting rules, cursors, and other structured data. So we use POST because it gives us a request body.

That workaround now has an official alternative.

In June 2026, the IETF published RFC 10008, defining a new HTTP request method called QUERY.

The same search can now be expressed as:

QUERY /products HTTP/1.1
Content-Type: application/json
Accept: application/json

{
  "filters": {
    "category": ["laptops", "tablets"],
    "price": { "min": 500, "max": 2000 },
    "inStock": true
  },
  "sort": [
    { "field": "rating", "direction": "desc" }
  ],
  "page": { "size": 50 }
}
Enter fullscreen mode Exit fullscreen mode

That looks like a small syntax change.

It is not.

QUERY tells the HTTP stack that this request:

  • can carry structured request content
  • is safe
  • is idempotent
  • can be retried as a query
  • has cacheable responses by specification

The interesting question in 2026 is no longer "what is QUERY?"

It is this:

Should you actually use it in production yet?

TL;DR

If you only remember five things:

  1. QUERY is standardized in RFC 10008 and registered as a safe, idempotent HTTP method.
  2. It is designed for read-only server-side queries where the input belongs in request content rather than a giant URI.
  3. Browser fetch() can send it today, but cross-origin requests require CORS preflight.
  4. QUERY responses are cacheable, but a correct cache key must include the request content. Browser caching support is still catching up.
  5. I would not migrate every POST /search endpoint today. I would consider QUERY for new APIs or controlled environments where I can test the complete request path.

That last point matters most.

A new HTTP method only works when every layer between the client and your handler agrees to let it pass.


The gap between GET and POST

A normal read fits GET perfectly:

GET /products?category=laptop&brand=apple&sort=price
Enter fullscreen mode Exit fullscreen mode

It is readable, shareable, easy to inspect, and works naturally with existing caching infrastructure.

Then requirements grow.

You add:

  • nested AND/OR conditions
  • multiple ranges
  • faceted filters
  • geospatial constraints
  • cursor pagination
  • aggregations
  • semantic search parameters

Eventually the query becomes a serialized data structure pretending to be a URL.

GET has practical URI limits

RFC 10008 points out that a request may travel through many independent systems with different URI limits. HTTP recommends support for request targets of at least 8,000 octets, but that does not give you one universal maximum across browsers, proxies, gateways, WAFs, CDNs, frameworks, and servers.

The problem is not finding a magic number.

The problem is that you do not control every hop.

Structured filters do not map cleanly to a URI

This is easy to understand:

{
  "and": [
    { "category": "laptop" },
    {
      "or": [
        { "brand": "Framework" },
        { "repairabilityScore": { "gte": 8 } }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Encoding the same structure into query parameters usually means inventing a convention every client must learn.

URIs are highly observable

URLs commonly appear in access logs, browser history, analytics systems, monitoring tools, and intermediary infrastructure.

A request body is not automatically private. Your infrastructure can log bodies too, and TLS is still mandatory.

But RFC 10008 explicitly notes that URIs are more likely to be logged or processed by intermediaries than request content. That matters when query inputs should not casually appear in a URL.

Why not just send a GET body?

Because HTTP does not define general semantics for GET request content.

RFC 9110 warns that GET content has no generally defined semantics and may be rejected by some implementations because of request smuggling concerns.

A private agreement between your browser and origin is not enough when several intermediaries sit between them.

This is the gap QUERY closes.


Important correction: POST /search was never "invalid"

A lot of QUERY explainers make this claim:

POST is for creating things, so POST /search is wrong.

That is too simplistic.

POST is broader than "create a resource." HTTP allows the target resource to process the enclosed content according to its own semantics. APIs have legitimately used POST for complex searches for years.

The real limitation is different.

A generic HTTP component cannot look at POST /search and know that your particular operation is safe and idempotent.

With QUERY, it can.

Safe

A safe method means the client is not requesting a state change to the target resource.

The server may still write logs, metrics, cache entries, or other incidental data. "Safe" describes the requested semantics, not the absence of every server-side write.

Idempotent

Idempotent means the intended effect of repeating the same request is the same as sending it once.

That becomes important when a connection fails halfway through a request.

An intermediary cannot generically assume that retrying POST is harmless.

QUERY explicitly provides that contract.


GET vs POST vs QUERY

Property GET POST QUERY
Safe by method semantics Yes Not guaranteed Yes
Idempotent by method semantics Yes Not guaranteed Yes
Request content has defined query semantics No Resource-specific Yes
Best for simple, shareable reads Excellent Usually unnecessary Usually unnecessary
Best for large structured read inputs Awkward Common workaround Designed for it
Response cacheable by HTTP semantics Yes Possible, with different rules Yes
Query input outside the URI No Yes Yes

The key point is simple:

QUERY does not replace GET.

GET remains the best choice for ordinary retrieval.

QUERY becomes interesting when the operation is still a read, but the input deserves a structured representation.


Four details in RFC 10008 that developers should know

1. Content-Type matters

The target URI defines the scope of the query. The request content and media type define the query itself.

QUERY /orders HTTP/1.1
Content-Type: application/json
Accept: application/json

{
  "where": {
    "status": ["paid", "shipped"]
  },
  "groupBy": ["country"],
  "limit": 100
}
Enter fullscreen mode Exit fullscreen mode

RFC 10008 requires the server to fail a QUERY request when Content-Type is missing or inconsistent with the request content.

That also gives useful error semantics:

  • 400 for missing or inconsistent media type
  • 415 for an unsupported query format
  • 422 for valid content that cannot be processed
  • 406 when the requested response format is unavailable

2. Accept-Query advertises supported query formats

RFC 10008 introduces Accept-Query.

Accept-Query: application/json, application/sql
Enter fullscreen mode Exit fullscreen mode

A client can also discover QUERY support through OPTIONS:

OPTIONS /products HTTP/1.1
Enter fullscreen mode Exit fullscreen mode

Possible response:

HTTP/1.1 200 OK
Allow: GET, QUERY, OPTIONS, HEAD
Accept-Query: application/json
Enter fullscreen mode Exit fullscreen mode

That gives an API a protocol-level way to say both "I support QUERY" and "these are the query representations I understand."

3. QUERY can participate in conditional requests

Because QUERY has retrieval-like semantics, it can work with validators and conditional requests.

That means a repeated expensive query can potentially use mechanisms such as If-Modified-Since and receive 304 Not Modified when appropriate.

4. A server can assign the query a GET-able URI

A successful QUERY can return a Location pointing to an equivalent resource.

HTTP/1.1 200 OK
Location: /queries/8f4b32a1
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode

The client can later use:

GET /queries/8f4b32a1
Enter fullscreen mode Exit fullscreen mode

This pattern is especially interesting for saved searches, analytics, expensive reports, and repeated complex queries.


Browser fetch can already send QUERY

Scripted fetch() requests can use QUERY because it is not a forbidden Fetch method.

const response = await fetch("https://api.example.com/products", {
  method: "QUERY",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    filters: {
      category: "laptop",
      minRating: 4.5
    },
    limit: 20
  })
});

const data = await response.json();
Enter fullscreen mode Exit fullscreen mode

One small 2026 gotcha: use uppercase QUERY.

The Fetch Standard currently normalizes only a fixed set of familiar methods such as GET and POST. QUERY is not yet in that normalization list, so lowercase query may be transmitted as lowercase instead of being automatically normalized.

Tiny detail, long debugging session.


Cross-origin QUERY requires CORS preflight

QUERY is not a CORS-safelisted method.

If your frontend and API are on different origins, the browser will send a preflight request first.

OPTIONS /products HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: QUERY
Access-Control-Request-Headers: content-type
Enter fullscreen mode Exit fullscreen mode

Your API needs to authorize it:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, QUERY, OPTIONS
Access-Control-Allow-Headers: Content-Type
Enter fullscreen mode Exit fullscreen mode

If your CORS config has a hard-coded method list such as:

GET, POST, PUT, PATCH, DELETE
Enter fullscreen mode Exit fullscreen mode

QUERY will fail even if your route handler understands it perfectly.

That leads to the biggest rollout lesson:

Application support is not the same as end-to-end support.


Caching is where QUERY gets difficult

RFC 10008 says QUERY responses are cacheable.

But there is a critical rule:

the cache key MUST incorporate the request content and related metadata.

Consider these requests:

QUERY /products
Content-Type: application/json

{ "category": "laptops" }
Enter fullscreen mode Exit fullscreen mode
QUERY /products
Content-Type: application/json

{ "category": "cameras" }
Enter fullscreen mode Exit fullscreen mode

The URI is identical. The query is not.

A cache that only keys on method plus URL could return laptop results for the camera request.

That is not a minor cache miss problem. It can become a data correctness or isolation problem.

A QUERY-aware cache needs something closer to:

method
+ target URI
+ request content
+ relevant representation metadata
+ Vary dimensions
Enter fullscreen mode Exit fullscreen mode

The RFC allows semantically insignificant differences to be normalized before calculating the key, but normalization itself is tricky.

For example, these JSON documents may be semantically equivalent:

{"category":"laptops","limit":20}
Enter fullscreen mode Exit fullscreen mode
{"limit":20,"category":"laptops"}
Enter fullscreen mode Exit fullscreen mode

Generic infrastructure should not assume every query format can be normalized the same way.

The spec is currently ahead of browser caches

An open WHATWG Fetch discussion notes that scripted QUERY requests work, but tests with Chrome and Firefox did not cache repeated identical QUERY requests in the body-aware way RFC 10008 permits.

So keep these two statements separate:

QUERY is cacheable by specification.

Your browser, CDN, proxy, or gateway currently implements QUERY caching correctly.

Do not assume the second from the first.


What should you test before production?

As of August 2026, the method itself is standardized, but ecosystem support remains uneven.

Layer Status to expect in 2026
RFC / IANA Standardized and registered
Browser fetch() Can send QUERY
Cross-origin browser request Requires CORS preflight
Browser QUERY caching Do not rely on it yet
curl Can send custom QUERY requests
Node.js Verify your deployed runtime with http.METHODS
Framework routing Support is arriving, verify your exact version
HTML forms Do not rely on declarative QUERY submission yet
CDN / WAF / gateway Must be tested explicitly

A valid request can still be rejected by:

  • reverse proxies
  • CDNs
  • WAFs
  • bot mitigation
  • API gateways
  • CORS middleware
  • service meshes
  • authentication middleware
  • observability pipelines

If you are testing Node, start with this:

import http from "node:http";

console.log(http.METHODS.includes("QUERY"));
Enter fullscreen mode Exit fullscreen mode

And curl can send the method directly:

curl -X QUERY https://api.example.com/products \
  -H 'Content-Type: application/json' \
  -d '{"category":"laptops","limit":20}'
Enter fullscreen mode Exit fullscreen mode

But localhost success proves very little.

Run that request through the same public hostname, CDN, gateway, firewall, and application path your real users will hit.


The production rollout checklist I would use

1. Confirm the operation is truly safe

Good candidates:

  • catalog search
  • analytics queries
  • log search
  • complex resource filtering
  • semantic search
  • data exploration

Bad candidates:

  • checkout
  • sending email
  • triggering deployments
  • marking notifications as read
  • anything whose requested semantics change application state

Do not use QUERY merely because an endpoint returns data.

2. Search the stack for method allowlists

Look for configurations like:

GET|POST|PUT|PATCH|DELETE|OPTIONS
Enter fullscreen mode Exit fullscreen mode

That may be hiding in a proxy, WAF, gateway policy, router, rate limiter, or test fixture.

3. Fix CORS deliberately

For browser clients across origins, add QUERY to allowed methods and test the preflight path.

4. Decide the cache strategy before launch

I would start with one of two options:

Option A: disable shared QUERY caching until every layer is verified.

Option B: implement a body-aware cache key deliberately and test it with adversarial inputs.

Never put QUERY behind a cache that ignores the request content.

5. Update observability

Make sure dashboards do not dump QUERY into OTHER.

Track request count, latency, error rate, cache behavior, preflight failures, and edge rejections separately.

6. Keep a fallback for public APIs

For a transition period, supporting both can be reasonable:

POST  /products/search   compatibility endpoint
QUERY /products          standards-based query endpoint
Enter fullscreen mode Exit fullscreen mode

That lets early adopters use QUERY without forcing every SDK and intermediary to upgrade at once.


So, should you replace POST /search?

My answer is not everywhere, and not simply because a new RFC exists.

I would use QUERY today when:

  • I am designing a new API or can evolve both client and server
  • the operation is genuinely read-only
  • the query is too complex for a clean GET URI
  • safe retry semantics matter
  • I can test the entire proxy and gateway path
  • I have a deliberate caching strategy
  • I can keep a fallback when interoperability matters

I would keep GET when:

  • the query is small
  • the URL is useful to bookmark or share
  • mature CDN and browser caching matters
  • maximum interoperability matters

I would keep POST when:

  • compatibility is more important than protocol purity
  • I cannot verify all intermediaries
  • the operation is not actually safe
  • migration creates more complexity than value

QUERY is not a reason to rewrite healthy APIs.

It is a better primitive for the next complex read endpoint you design.


A note from Techifive

At Techifive, we design and build custom software systems, including REST and GraphQL APIs, web applications, cloud infrastructure, edge delivery, security, and performance optimization.

HTTP QUERY is a good example of why production API design does not stop at the controller. A request crosses clients, CORS policy, gateways, caches, security layers, observability systems, and application code. A standards-compliant endpoint can still fail if one layer in that chain is not ready.

For teams modernizing an API platform, the useful question is not only "does my framework support this?" It is "does the complete production path support it safely and predictably?"

That is the level at which architecture decisions should be evaluated.


Final takeaway

QUERY is now a real part of HTTP, not an experimental naming convention.

It gives developers something that was missing for a long time: a standardized way to send structured query content while explicitly preserving safe and idempotent semantics.

But standards adoption happens in layers.

Your browser may send QUERY while your CORS policy blocks it.

Your application may route it while your WAF rejects it.

Your origin may understand it while your cache keys it incorrectly.

So yes, learn it.

Yes, prototype it.

And for the right new API, consider using it.

Just test the complete request path before replacing every POST /search in production.

If you have already tested HTTP QUERY, which layer in your stack was the first to reject it?

That answer is probably more useful to other developers right now than another explanation of what an HTTP method is.


References

Top comments (0)