The new safe+idempotent HTTP method that lets you ship SQL inside the request body was published with a Security Considerations section five paragraphs long. Five.
So. RFC 10008.
In June 2026, the IETF published RFC 10008 — The HTTP QUERY Method. It defines a new HTTP verb that sits in the gap nobody realized was embarrassing the protocol for thirty years: the gap between GET (safe, idempotent, cacheable, no body, URI-logged) and POST (has a body, not safe, not idempotent, not automatically cacheable, looks dangerous).
The motivation is real. Search engines, graph databases, AI orchestration layers, observability backends — basically every modern HTTP service — has been performing queries by stuffing stateful filters into a URI or, worse, by using POST and hoping everyone agrees the operation is idempotent. GET /feed?q=foo&limit=10&sort=-published works until your q is a 200 KB JSONPath expression, at which point you’re fighting URL length limits, encoding overhead, and the fact that every reverse proxy on the planet logs the URI by default.
So the QUERY method was inevitable. It took fourteen drafts to get past IESG and a name change along the way (early drafts used SEARCH, which the WebDAV folks already claimed for XML-only).
The verb’s properties are simple and, on paper, sensible:
- Safe — does not change the target resource’s state.
- Idempotent — replaying it yields the same result.
- Body-bearing — query inputs live in the body, not the URI.
- Cacheable — cache key incorporates body + media type.
- Discoverable —
Accept-Queryresponse header advertises supported query formats.
You can send a SQL query to your data API and have it retry automatically on a dropped connection. You can cache the result by hashing the body. The browser will preflight it because it’s not on the CORS safelisted list. The world, in theory, gets a better way to ask questions.
In theory.
What the RFC Gets Right
Before we get to the body of this review — a nod to what the spec actually gets right.
1. The honest observation about logging asymmetry.
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 logging of the URI might motivate the use of QUERY over GET.
This is the entire reason the verb exists and the RFC actually says it out loud. If you’ve ever seen a customer’s SSN end up in a SIEM because a junior dev put it in a GET parameter and the WAF logs all 200 responses, you understand why moving that data into the body is, by default, a privacy upgrade. The RFC doesn’t oversell this — it merely flags the asymmetry and lets deployers make the call.
2. The redirect exception for 301/302.
The exceptions for redirecting a POST as a GET request after a 301 or 302 response do not apply to QUERY requests.
RFC 9110 allows POST → GET downgrade on certain redirects, which is the basis for a small but persistent class of CSRF and request-smuggling bugs. QUERY explicitly opts out. Small detail. Big implication. Whoever reviewed that paragraph did us all a favor.
3. The Content-Type validation rule.
Servers MUST fail the request if the Content-Type request field is missing or is inconsistent with the request content.
The RFC bans content-sniffing. No, you cannot infer that {"sql":"..."} is application/sql because it looks SQL-ish. No, you cannot fall back to application/octet-stream. The client told you what it sent. Trust it or reject the request. This is the kind of explicit anti-pattern call that prevents a generation of confusion-induced parser bugs.
4. The non-safelisted CORS note.
A QUERY request from user agents implementing Cross-Origin Resource Sharing (CORS) will require a “preflight” request, as QUERY does not belong to the set of CORS-safelisted methods.
Browsers will preflight every cross-origin QUERY. That kills a category of accidental cross-origin attacks for free, but it also kills a category of harmless integrations that expected to work like GET. Trade-off made explicit.
5. The cache-key construction rules.
The cache key for a QUERY request MUST incorporate the request content and related metadata.
If a cache implements this correctly — and the RFC tells them they have to — then cross-query collisions are forbidden by spec. A reasonable design.
So far, so good. Now let me show you why none of this matters for the next eighteen months of production traffic.
A Note on Compatibility
Before we get to the security observations, one operational reality has to be on the table: as of June 2026, no major CDN, WAF, or reverse proxy ships native QUERY support.
The protocol is standards-track. The implementations are not. That’s a bigger security story than anything in §4.
What Your Web/App Server Should Do (Security Perspective)
The next eighteen months will be a settling period. Production servers will receive QUERY requests from new clients while every intermediary in the path makes up its own behavior. Here’s what your stack should be doing now, before the dust settles:
1. Web framework / app server — register QUERY as a first-class method, but do not trust it.
- Add
QUERYto the allowed-method registry explicitly. Don’t rely on default-allow or default-deny — both have failure modes. - In your route table, mount QUERY handlers at the same paths as your GET handlers, but with a separate code path that runs the body through your query-language interpreter. Never let a generic GET handler process a QUERY body.
- Set a per-route
Content-Lengthcap. The RFC’s worked examples are < 4 KB. Most realistic query bodies are < 64 KB. Anything larger is almost certainly an attack or a misconfigured client. Cap at 64 KB and reject with 413. - Set a per-request compute budget: timeout, memory cap, CPU cap. The body is opaque to your framework — without a budget, one QUERY can DoS the worker pool.
2. Reverse proxy (nginx, Envoy, HAProxy, Traefik) — fail fast on the unknown.
- As of this writing, none of these forward QUERY by default with body intact. Some strip the body, some buffer it to a 1 MB limit, some return 405, some crash. Test your specific build.
curl -X QUERY --data-binary @<large-file> http://your-proxy/your-endpointand observe. - Configure explicit
client_max_body_size(nginx) /max_request_bytes(Envoy) for routes that accept QUERY. Don’t rely on the global default. - Drop the
Expect: 100-continuehandshake for QUERY to avoid double-buffering the body. - Forward the body verbatim. Don’t let your proxy silently re-encode it (chunked → content-length, gzip on a body the upstream will re-gzip). Body-hash cache keys depend on byte-equality.
3. CDN (Cloudflare, Akamai, Fastly, CloudFront) — assume cache poisoning until proven otherwise.
- Until the CDN explicitly documents cache-key construction including body + Content-Type, do not cache QUERY responses at the shared tier. Serve them from origin or from a private edge cache.
- For responses that must be cached (low-cardinality queries, public data): verify the
Vary: Accept-Query, Content-Type, Content-Encodingheader is honored. If your CDN only honorsVaryon response headers and not request headers, QUERY responses will collide. - Set
Cache-Control: private, no-storeon QUERY responses containing tenant-scoped or PII data. The CDN cannot enforce tenant isolation in the cache key — your origin must.
4. WAF (Imperva, AWS WAF, F5, Barracuda, Cloudflare WAF) — write signatures before attackers do.
- Add explicit rules for QUERY: method-specific request size limits, body-length limits, rate limits.
- Write signatures for QUERY-borne attack patterns: XSLT
document(), SQLUNION SELECT, JSONPath filter chains with regex DoS patterns, XQuery external entities. - Configure the WAF to parse the body for QUERY-supported media types (
application/sql,application/jsonpath,application/xslt+xml) and apply injection-detection rules. A WAF that only inspects URIs will miss every QUERY-borne attack. - Block QUERY entirely at the edge if you don’t yet have a server-side handler. Better to 403 than to 405-an-unknown-method that bypasses CSRF middleware.
5. CSRF middleware (Django, Rails, Spring Security, OWASP CSRFGuard) — do not auto-exempt QUERY.
- The auto-exempt logic in most frameworks keys off the “safe methods” list, which includes everything RFC 9110 calls safe. QUERY is safe. The framework will exempt it. Override the default. Treat QUERY like POST for CSRF purposes: require tokens, require same-site cookies, require origin verification.
- Document this override prominently in the framework’s CSRF config. The next developer to touch it will revert it without understanding why.
6. AuthZ filters (OPA, Casbin, Spring Security, ASP.NET policies) — authorize based on body content, not just method+URI.
- A GET request to
/api/usersis one authorization decision. A QUERY request to/api/userswith body{"tenant":"any","filter":"*"}may be a different authorization decision entirely. If your policy only inspects method+URI, a low-priv tenant can craft a body that bypasses tenant scoping. - For QUERY endpoints, ship body-content policies alongside URL-pattern policies. E.g. “QUERY to
/api/usersis allowed only if bodytenant_id == auth.tenant_id.” - Audit logs must capture the body (or a redacted hash) for QUERY. If your audit pipeline only captures method+URI+status, you have a §6 DLP blind spot (below).
7. DLP / SIEM — extend coverage to bodies now, not after an incident.
- DLP rules tuned on
GET ?ssn=patterns need a parallel set tuned on QUERY body patterns. Same data, different channel. - SIEM correlation rules need a new category: “anomalous QUERY response size,” “QUERY to sensitive endpoint outside business hours,” “QUERY body containing keywords from data classification dictionary.”
- Log search backends that exclude bodies for cost reasons need an explicit exception list for QUERY-bearing endpoints. Otherwise your incident response will show
QUERY /api/users 200 543210with no indication of which user, which columns, which filter.
8. Load balancers — preserve QUERY semantics through the hop.
- AWS ALB, GCP GLB, and most L7 balancers terminate HTTP and re-forward. Verify that QUERY method is preserved (not downgraded to GET) and that the body is forwarded intact and uncompressed if the upstream expects a specific Content-Encoding.
- Health checks should not use QUERY. Stick to GET or HEAD for health probes — a slow QUERY evaluation on a health check can mark healthy backends unhealthy.
The Minimum Viable QUERY-Compatible Stack
If you’re starting from scratch and want to deploy QUERY in the next six months, the minimum stack is:
- Origin: web framework with explicit QUERY handler, body-size cap, compute budget, sandboxed query interpreter
- Reverse proxy: body-intact forwarding with size cap, no body re-encoding
- CDN: bypass for QUERY routes until cache key correctness is proven, or
private, no-storeresponses - WAF: QUERY method allowed, body-inspection rules for SQL/JSONPath/XSLT
- CSRF middleware: QUERY explicitly non-exempt
- AuthZ filter: body-content policies
- SIEM: QUERY body logging on sensitive endpoints
- DLP: QUERY body scanning on sensitive endpoints
- Load balancer: method-preserving, body-intact forwarding
That’s nine layers, each of which needs an explicit decision. The protocol is one sentence. The deployment is a checklist. This is why standards-track does not mean production-ready.
The Threat Model the RFC Doesn’t Have
Here’s Section 4 — Security Considerations — in full:
The QUERY method is subject to the same general security considerations as all HTTP methods as described in [HTTP].
It 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, as 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 logging of the URI might motivate the use of 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.
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 normalization results in a false positive.
A QUERY request from user agents implementing Cross-Origin Resource Sharing (CORS) will require a “preflight” request, as QUERY does not belong to the set of CORS-safelisted methods.
That’s it. Five paragraphs. Compared to RFC 9110 §17 (which is the Security Considerations for HTTP itself, sprawling and reference-rich) or even RFC 9535 §6 (JSONPath, which has eight paragraphs dedicated to sandboxing, injection, and resource exhaustion), this is bare.
For a method that explicitly normalizes SQL, JSONPath, XSLT, XQuery, and “any other query language an implementation defines” as request bodies, five paragraphs is not enough.
What’s missing:
- Zero threat modeling. No enumeration of injection, SSRF, CSRF/CORS misconfiguration, cache poisoning, storage DoS, logging leakage, redirect exfiltration.
- No query-language sandboxing guidance. Zero mention of parameterized SQL construction, libxml2/libxslt hardening, JSONPath engine selection, expression-length caps.
- Asymmetry of “SHOULD.” Using advisory language instead of an explicit
MUST NOTfor sensitive data leakage in Location headers. The single paragraph that addresses body-content exfiltration via response headers is the only mitigation in the entire section — and it’sSHOULD, notMUST. - No
no-store/privatecache directive guidance. When the QUERY response contains PII, the RFC says nothing about whether to mark it uncacheable. The cache key MUST include body content; the RFC does NOT say “and the response MUST NOT be cached if the body contained PII.” - No operational budgets. Total silence on execution timeouts, memory caps, request-size limits, rate-limiting, or per-tenant quotas. Bodies can be huge. Query execution can be expensive. The RFC says nothing.
- No mention of encryption at rest for stored query results.
Location: /stored-queries/42is a persistent server resource. The RFC doesn’t say what state that resource should be in. - No explicit warning about the “safe + creates resources” inconsistency. §2 says QUERY is safe. §2.3/§2.4 then says the server may create Location resources. That’s a contradiction the RFC papers over with a “MUST NOT predictable from request” clause that’s about as enforceable as a “be excellent to each other” bumper sticker.
- No cross-origin redirect safety analysis for §2.5. A QUERY redirected via 301/302 re-issues with the body attached to the new origin. The RFC documents the method-preservation but does not warn about exfiltration.
- No interaction note with WebDAV even though Appendix B explicitly references SEARCH/REPORT/PROPFIND. Two safe+idempotent+body-bearing query methods with overlapping semantics is going to be a source of operational drift.
- No intermediary-compatibility discussion. The whole thing falls apart if your CDN doesn’t recognize the verb. The RFC trusts the ecosystem. The ecosystem is not ready.
That’s not me being harsh. That’s me counting the paragraphs and comparing them to peer RFCs. The security section is literally the size of a security section from 1998.
Observation 1: Injection Becomes the Default
This is the headline finding and it deserves its own section.
The RFC ships with worked examples in SQL, JSONPath, and XSLT. These are not abstract references. They are in the appendices, formatted as canonical request/response pairs, with Accept-Query response headers showing the production-recommended media types.
application/sql, application/jsonpath, application/xslt+xml.
The RFC is, in effect, publishing a standardized way for clients to send SQL/XPath/JSONPath to servers and have the servers execute it. The mitigation is that QUERY is safe+idempotent and servers are expected to reject unsafe input.
But safe for the target resource does not mean safe for the underlying data store.
SQL injection in a QUERY body is no different from SQL injection in a POST body or in a q GET parameter — except that:
- Many CSRF mitigations auto-skip safe methods. They will skip QUERY. Wrong.
- Many “structured input” parsers auto-trust the body because they assume the client built it carefully. False.
- The QUERY method makes it tempting to expose raw SQL as a feature: “send us a SQL query, we’ll execute it against your tenant scope.” Done wrong, this is a BOLA engine.
XSLT is the scariest of the three, and the most damning indictment of the RFC’s editorial judgment, because the spec’s own Appendix A.5 ships XSLT as a canonical example:
QUERY /rfc-index.xml HTTP/1.1
Host: example.org
Content-Type: application/xslt+xml
Accept: text/csv
...Query content using XSLT...
XSLT 3.0 supports:
document()— fetches arbitrary URLs. SSRF.xsl:import/xsl:include— pulls external stylesheets. SSRF, supply chain.- Custom extension functions — RCE depending on processor.
xsl:evaluate— dynamic XPath evaluation on attacker-supplied strings.
A server that accepts application/xslt+xml bodies without disabling these features has handed the attacker an SSRF and RCE primitive. The RFC mentions XSLT in passing. It does not mention enable_document_function=false. It does not mention entity resolver configuration. It does not mention sandboxing.
Call this what it is: a time-travel vulnerability. The IETF just imported 2012’s favorite XML attack surface — XXE, external DTDs, the whole document('http://169.254.169.254/...') genre — into 2026’s modern API stack, with a worked example in the RFC’s own appendix, with no neon-flashing warning, no MUST NOT on extension functions, and a security section that doesn’t mention XML once. The OWASP Top 10 retired XXE from the headline list in 2017 because the industry had supposedly learned to disable DTDs and external entities. The IETF just standardized the opposite. Every server that implements the RFC’s Appendix A.5 example as-written is the OWASP 2017 playground, fresh and reloaded.
This isn’t a footnote. This is editorial malpractice at standards-track scale. The IETF reviewed fourteen drafts of this RFC. In none of them did anyone write MUST disable document() or MUST NOT accept application/xslt+xml without explicit server-side allowlisting. The security considerations section has five paragraphs. None of them are about XML.
JSONPath (RFC 9535) has a smaller attack surface but is not free. Filter expressions like $..[?@.x == @.y] compile to regex/eval on untrusted input. The JSONPath spec has its own §6 Security Considerations that the QUERY RFC could have referenced and didn’t.
Mechanism: Servers MUST fail the request if the Content-Type request field is missing or is inconsistent with the request content. That’s a parsing check. It’s not an execution check. The body can be valid XML and contain malicious XSLT. The body can be valid SQL and contain a UNION SELECT password FROM admins. The RFC is silent on what to do with a body that parses but evaluates dangerously.
Control gap: The RFC says nothing about parameterized query construction, sandboxed XSLT execution, or expression-length caps. Implementations are on their own.
Observation 2: Safe-but-Creates-Resources (with Bonus Auto-DDoS)
§2 — QUERY requests are safe with regard to the target resource.
§2.3 — A successful response (2xx) can include a Content-Location header field.
§2.4 — A server can assign a URI to the equivalent resource of a QUERY request.
Read those three paragraphs in sequence and you’ll notice that the same method that is safe is also allowed to create new server-side resources. RFC 9110 §9.2.1 permits this for safe methods only if the new resource’s URI is not predictable from the request.
That means: each QUERY that triggers Location: /stored-queries/42 allocates a new resource on the server. The body of the QUERY — the SQL, the JSONPath, the XSLT — gets baked into a stored query resource that may persist for hours, days, or forever depending on the implementation’s TTL policy.
Now replay the QUERY 100,000 times from a botnet.
Without a TTL, without a dedupe-by-content-hash, without a per-tenant cap, every “safe” QUERY becomes a storage amplification vector. The verb is safe from the perspective of the target resource. From the perspective of the filesystem, it is a write. A write that the client thinks is idempotent and retries automatically on connection failure.
Multiply that by a CDN edge with replication, and you’ve invented a new way to fill a disk by sending carefully-crafted SQL that the server helpfully stores.
The RFC’s only mitigation is implicit: the URI SHOULD be chosen such that it does not include any sensitive portions of the original request content. That mitigates information leakage, not storage growth.
The Idempotent-Retry Auto-Amplification Layer
But wait. It gets worse. Because QUERY is explicitly defined as idempotent, modern HTTP client libraries are RFC-authorized to automatically retry failed requests on network drops without consulting the application layer. So are reverse proxies. So are service meshes. So are browser fetch implementations with their default retry policies. So is anything that wraps cURL with --retry.
The RFC didn’t just normalize a body-bearing safe method. It normalized a body-bearing safe method whose retries are protocol-correct.
Now picture an attacker who sends a single QUERY containing a 200 KB JSONPath expression that takes 30 seconds to evaluate. The request hits your gateway. The gateway has a 25-second upstream timeout. The gateway returns 504 to the client. The client — following the HTTP spec, doing exactly what the IETF told it to do — replays the request automatically. Three times. Five times. With exponential backoff.
Your origin isn’t being flooded by a botnet. Your origin is being flooded by its own intermediary stack helpfully replaying attacker payloads. The retry behavior the protocol authorizes is exactly the retry behavior that turns one expensive query into five, ten, fifty expensive queries against a server that is now also busy storing the results of each replayed attempt under different Location URIs.
A botnet sending 1,000 QPS of slow QUERY requests becomes 5,000-50,000 QPS at the origin, plus N² stored-result resources, plus N² entries in the dedupe index you probably haven’t built yet.
This is the observation that makes the storage vector scary. Not the storage. The amplification. The protocol’s own safety properties become the attacker’s delivery vehicle. The retry mechanism is the load generator. The safe-method classification is the green light that every well-behaved client and proxy will respect.
Mitigation is non-trivial: idempotency keys in a header (RFC 9110 §10.2.2 doesn’t define them but allows them), content-hash-based dedup at the gateway, hard caps on concurrent retries, aggressive TTL on stored results. The RFC mentions none of these. The RFC just defines the verb and trusts the ecosystem to figure out the rest.
Observation 3: The Logging Asymmetry, Done Wrong
The RFC’s strongest insight is also the most operationally fragile.
The promise: bodies are not logged by default. URIs are. Therefore, putting query inputs in the body is a privacy upgrade.
The reality: bodies are not logged by default, but they are:
- Cached in shared-memory caches that get snapshotted during heap dumps.
- Mirrored to S3 by APM agents that sample “slow requests.”
- Indexed by log search backends that the security team forgot to exclude.
- Echoed by TRACE if anyone misconfigures a proxy.
- Surfaced in
LocationandContent-Locationresponse headers when the server creates stored-query resources.
The RFC explicitly warns about the last one and uses SHOULD:
the URI SHOULD be chosen such that it does not include any sensitive portions of the original request content.
Should. Not MUST. And no example of what “sensitive portions” looks like.
If your SQL body is SELECT * FROM patients WHERE ssn LIKE '%query%', the resulting /stored-queries/abc123 URI is fine — it’s an opaque hash. But if your body is {"ssn":"123-45-6789", "dob":"1980-04-12"}, and you naively hash it to /stored-queries/<sha256(body)>, then anyone who can guess the input space (a small one, in this case) can enumerate the stored query URIs and exfiltrate the hash space to a separate attack surface.
The mitigation is mandatory content-hash dedup with no per-record URI emission. The RFC should have said so.
Observation 4: CORS, But the Frameworks Don’t Know (and the Workarounds Are Worse)
A QUERY request from user agents implementing Cross-Origin Resource Sharing (CORS) will require a "preflight" request, as QUERY does not belong to the set of CORS-safelisted methods.
Good. Every cross-origin QUERY gets a preflight. Every framework that auto-generates CORS responses needs to add QUERY to Access-Control-Allow-Methods for the preflight to succeed.
This is the part where I have to look at how every web framework in production has handled similar method additions historically, and the news is not reassuring.
When PATCH was added to HTTP, frameworks spent two years misconfiguring it. When WebSockets went through, half the CORS middleware on the internet blocked them. QUERY is arriving on top of:
- Express, Fastify, Hapi, Koa — middleware that ships Allow lists keyed on method strings.
- Django, Flask, Rails — CSRF middleware that exempts “safe” methods.
- Spring, ASP.NET, Quarkus — authz filters that map HTTP methods to permission strings.
- Cloudflare, Akamai, Fastly, CloudFront — edge rules that match on method.
- Imperva, AWS WAF, F5, Barracuda — WAF signatures that often key on method.
- Datadog, Splunk, Elastic, Sumo — log pipelines that filter on method for noise reduction.
Every single one of those needs an update to handle QUERY correctly. None of them will be updated uniformly. Some will treat QUERY as GET. Some will treat it as POST. Some will drop it. Some will forward the body but strip the Accept-Query header. Some will buffer the body into a 1MB limit and return 413 for valid requests. Some will log the body and silently exfiltrate PII to a SIEM that the security team never opted into.
The CORS Paradox
Here’s the part the RFC doesn’t address and the developer community will route around within six months.
Single-page apps today generate significant cross-origin traffic. Every QUERY from https://app.example.com to https://api.example.com requires a preflight OPTIONS round-trip before the real request. That’s an extra RTT for every QUERY call, plus the latency tax on every preflight that misses the cache.
SPAs are latency-sensitive. Backend developers will get frustrated. The preflight overhead will land in a perf review, an SLA, a postmortem. The fix, every time, will be one of:
Access-Control-Allow-Methods: *— wildcard. Now every cross-origin method, including DELETE, TRACE, and CONNECT, is preflightable from any origin. The CORS guardrail is gone.Access-Control-Allow-Methods: GET, POST, QUERY, PUT, PATCH, DELETE— explicit but expanded. TheAllow-Origin: *paired with this wildcard-method list is functionally identical to no CORS at all.- Cache the preflight for 24 hours via
Access-Control-Max-Age: 86400— silences the latency complaint, hides the security implications behind a stale header, and lets the wildcarded config persist for a day without re-checking. - “Just turn off CORS for QUERY endpoints” — happens in dev environments, leaks into staging, leaks into prod.
Every one of these workarounds undoes the very protection the RFC invoked. The preflight was supposed to force the developer to make an explicit, visible, reviewable decision about cross-origin QUERY. The preflight will be eliminated the moment it becomes inconvenient. The protection survives only in the textbooks.
The mitigation is not “configure your CORS properly.” The mitigation is “make the preflight fast enough that nobody wants to bypass it.” That is a CDN-level problem, not an RFC-level problem.
Observation 5: Cache Poisoning by Mis-normalization
The cache key for a QUERY request MUST incorporate the request content and related metadata.
Good. But then:
To improve cache efficiency, caches MAY remove semantically insignificant differences from request content and related metadata first.
This is where caches go to die.
JSON key ordering. Whitespace. Comment styles. Trailing commas in CSV. BOM markers. Date format variants (2026-06-24 vs 2026-06-24T00:00:00Z). All “semantically insignificant” in some sense. All attack-surface for cache poisoning.
A cache that doesn’t understand the query format can over-normalize and collide two distinct queries into one cache key. False positive. Wrong data returned to wrong user. In a multi-tenant system, this is a cross-tenant data leak that the cache author wrote in good faith.
The RFC notes this in one sentence:
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 normalization results in a false positive.
That’s a sentence. It should be a section with examples, including a worked cross-tenant cache key collision.
There’s also the Vary header question. RFC §A.5 shows the correct Vary field for QUERY:
Vary: Accept-Query, Content-Encoding, Content-Type
But most CDNs do not honor Vary on request bodies. Most CDNs don’t even read request bodies — they cache based on URI + selected response headers. A CDN that caches QUERY responses by URI alone will return one tenant’s data to another tenant’s request with no error code to indicate the mistake.
The mitigation is “don’t cache QUERY at the shared CDN tier until you’ve proven your cache key is correct.” The RFC says nothing about this.
Observation 6: DLP, SIEM, and the Audit Blind Spot
This is the finding I think security teams will discover last and regret first.
The universal standard for HTTP audit logging is:
<timestamp> <method> <uri> <status> <bytes> <user-agent>
Five fields. The body is not one of them. The body is expensive to log. Most teams only log bodies when debugging a specific incident.
QUERY moves the security-relevant input from the URI (which is logged) to the body (which is not). That is the entire point of the method. It is also a categorical regression in incident response.
When a QUERY-based exfiltration happens, the audit log will show:
2026-06-24T10:00:01 QUERY /api/users 200 543210 chromium/120
The auditor sees 543210 bytes returned to /api/users. They have no idea which user, which columns, which filter. The body is gone. The SIEM has no rule for “large QUERY response to a sensitive endpoint” because no one’s SIEM has a category for it.
DLP rules tuned on GET ?ssn= patterns miss QUERY bodies entirely. So do data classification scanners that key on URI parameters. So do anomaly detection models trained on q=* GET parameter distributions.
The mitigation is to explicitly log QUERY bodies for sensitive endpoints, with redaction, and to teach the SIEM that body logging on QUERY is the new normal. The RFC does not mention this. If you operate a security-sensitive QUERY endpoint, your auditors will not know to ask for body logs until after an incident.
Observation 7: Redirect Exfiltration
§2.5 — In some cases, the server may choose to respond indirectly to the QUERY request by redirecting the user agent to a different URI.
For 301/308/302/307, the redirect inherits the method. A QUERY request that gets a 302 response will be re-issued as a QUERY to the new target — with the original body attached.
A malicious server, or a malicious intermediary that rewrites Location headers, can therefore redirect a QUERY request to an attacker-controlled origin with the body in tow. This is the same exfiltration pattern that affects POST on legacy browsers, but POST has a decades-old ecosystem of CSRF defenses and tooling. QUERY has none of that yet.
303 is safe — body is dropped on conversion to GET. The RFC documents this. But the user-agent community has had thirty years of muscle memory for handling 302 on POST-as-form-submission, and that muscle memory is wrong for QUERY. Some user agents will follow 302 by re-issuing the original method and body. Some will downgrade to GET. The behavior is not consistent across browsers and has not been tested at scale.
The mitigation is to forbid redirect chains on QUERY-sensitive endpoints and to inspect Location response headers in the same way you would for Set-Cookie. The RFC should have said so.
Observation 8: SSRF, Just Free, No Assembly Required
XSLT’s document() function is the gift that keeps on giving. document('http://169.254.169.254/latest/meta-data/') reads cloud instance metadata. document('file:///etc/passwd') reads local files. document('http://internal-admin-api/...') reaches into private network space.
A server that accepts application/xslt+xml as a QUERY body format has handed the attacker an SSRF primitive against the origin’s network position — for free.
The RFC mentions XSLT in its Appendix A.5 example. It does not say disable document function. It does not say configure entity resolver to deny external resources. It does not say sandbox the XSLT processor. It does not say require XSLT to be signed by the resource owner.
This is the part where I want to be generous and assume the RFC authors intended this to be implementation-defined. But “implementation-defined” is how SSRF ends up in CVE databases, and the standards-track RFC is the right place to draw a line.
Observation 9: Storage DoS, Compute DoS, Parser DoS
Three flavors of denial-of-service that QUERY opens up:
Storage: every Location resource = persistent server state. Unbounded.
Compute: query evaluation on attacker-controlled body. No guidance on timeouts, expression-length caps, or rate limits.
Parser differentials: Accept-Query is a Structured Field (RFC 9651). RFC 9651 parsers have a long CVE history — integer overflow in length-prefixed integers, O(n²) parsing on List fields, integer underflow on Dictionary fields. The Accept-Query field is the new attack surface for parser bugs.
And Range requests. §2.8 acknowledges they’re “little value” for QUERY but doesn’t forbid them. A large result + Range = memory amplification. Why is this in the spec?
Observation 10: The WebDAV-Style Method Name Confusion
The verb is called QUERY. The method registry already contains PROPFIND (RFC 4918), REPORT (RFC 3253), and SEARCH (RFC 5323), all of which are also safe, idempotent, and body-bearing.
SEARCH and QUERY are likely to be confused for years. A senior engineer who learned WebDAV in 2003 will reach for SEARCH by default. A junior engineer who learned REST in 2015 will reach for QUERY by default. APIs that already speak SEARCH will continue to accept it, alongside QUERY, until one of them is deprecated. The coexistence is going to be messy.
The RFC acknowledges this in Appendix B and offers a brief justification for not reusing SEARCH. That’s appropriate. But the operational impact — two methods with overlapping semantics, different media type defaults, different content-handling rules — is going to be a source of bugs and security drift for the next decade.
A Defense-in-Depth Checklist for Deployers
If you’re shipping QUERY before your tooling catches up, here’s the minimum:
Authentication / Authorization
- Treat QUERY like POST for CSRF purposes. Require tokens or same-site cookies. Never exempt.
- Authorize based on body content, not just URI + method. If tenant scoping is in the body, enforce it.
- Never put
QUERYinAccess-Control-Allow-Methods: *or pair it withAllow-Origin: *.
Resource Exhaustion
- Hash bodies to deterministic
LocationURIs. Dedupe. Cap TTL (24h is reasonable). - Set per-request compute budget (timeout + memory cap).
- Cap request body size at the edge. The RFC’s example bodies are < 4 KB. Most realistic query bodies are < 64 KB. Anything bigger is probably an attack.
Caching
Cache-Control: private, no-storeon responses that contain PII until cache key correctness is proven.- Validate that your CDN actually includes the body in the cache key. If it doesn’t, don’t cache QUERY at the shared tier.
- Add
Vary: Accept-Query, Content-Type, Content-Encodingto QUERY responses and verify the CDN honors it.
Query Language Sandboxing
- XSLT: disable
document(), external entities, extension functions. Configure a strict entity resolver. - SQL: parameterized query construction. Reject raw expression bodies from untrusted users.
- JSONPath: pick an RFC 9535-compliant engine. Watch for ReDoS on filter expressions.
Logging / Detection
- Log QUERY bodies for sensitive endpoints with redaction. Update SIEM rules to look for body exfiltration patterns.
- Update DLP rules to scan QUERY bodies, not just URI parameters.
- Monitor
Locationresource creation rate. Alert on anomalies.
Intermediaries
- Set explicit
Content-Lengthcap at the edge. - Send
Expect: 100-continueto fail fast on large bodies. - Configure WAF rules that recognize QUERY-specific attack patterns (XSLT
document(), SQL UNION, JSONPath filter chains).
Operational
- Update your framework’s Allow list to include QUERY.
- Update your CSRF middleware to NOT exempt QUERY.
- Update your authz filter to consider body content for QUERY endpoints.
- Update your WAF signatures to include QUERY-specific rules.
- Update your DLP rules to scan QUERY bodies.
- Update your SIEM correlation rules to treat QUERY as a query channel, not a benign GET.
What I’d Tell the RFC Authors
This is a good RFC. The design is sound. The motivation is real. The redirect exception is correct. The Content-Type validation rule is correct. The cache-key construction rule is correct.
But the Security Considerations section is a stub. For a method that ships as the standardized way to send SQL/JSONPath/XSLT to a server, five paragraphs is not enough.
If I had five minutes with the editors of this RFC, I’d ask them to consider an errata that:
- Expands §4 from five paragraphs to a full threat model.
- Adds a “Query Language Security” subsection with at least a sentence on SQL parameterization, XSLT sandboxing, and JSONPath engine selection.
- Changes the
SHOULDon Location URI sensitivity toMUST NOT. - Adds normative guidance on
Cache-Control: private, no-storefor QUERY responses containing PII. - Adds a section on intermediary compatibility with a recommended
Content-Lengthcap and a note thatno-transformis advisory only. - Adds a section on body logging with a recommendation for deployers.
- Adds a cross-origin redirect safety analysis for §2.5.
- Adds an interaction note with WebDAV’s SEARCH/REPORT/PROPFIND.
Most of those are paragraphs, not pages. None of them require protocol changes. They require the security review the RFC skipped.
The Real Headline
Here’s the thing.
The QUERY method is going to ship. CDNs will add support within a year. Frameworks will update their method registries. CSRF middleware will be patched. SIEM vendors will add body-logging categories. The standards ecosystem will catch up.
In the meantime — in the next eighteen to thirty-six months — the operational risk lives in the gap between the protocol and the intermediaries. The protocol says one thing. The CDN does another. The WAF does a third. The DLP does a fourth. The SIEM does a fifth. And the body of the QUERY request — the actual security-relevant content — falls into the gap, unlogged, unscanned, and uncached-correctly.
If you’re deploying QUERY, deploy it like POST. Log the body. Sandbox the interpreter. Cap the resources. Don’t trust the CDN until you’ve proven its cache key is correct. Don’t trust the framework until you’ve verified it doesn’t exempt QUERY from CSRF.
And read the RFC’s five-paragraph Security Considerations section. Then write your own. Longer.