<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Web Standards |</title><link>https://leonardosalles.com/tags/web-standards/</link><atom:link href="https://leonardosalles.com/tags/web-standards/index.xml" rel="self" type="application/rss+xml"/><description>Web Standards</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Tue, 23 Jun 2026 00:00:00 +0000</lastBuildDate><image><url>https://leonardosalles.com/media/logo_hu_8c3a2f052b7b22e7.png</url><title>Web Standards</title><link>https://leonardosalles.com/tags/web-standards/</link></image><item><title>HTTP QUERY: A Safe, Idempotent Request with a Body</title><link>https://leonardosalles.com/blog/http-query-method/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://leonardosalles.com/blog/http-query-method/</guid><description>&lt;p&gt;In June 2026, HTTP gained a new standardized method: &lt;code&gt;QUERY&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Published as
, it addresses a problem API developers have worked around for years: how can we send a complex, read-only query when it is too large or structured for a URL?&lt;/p&gt;
&lt;p&gt;The tempting explanation is:&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; is a &lt;code&gt;GET&lt;/code&gt; with a body.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is a useful first intuition, but it is not technically accurate. A &lt;code&gt;GET&lt;/code&gt; requests a representation of the resource identified by the URI, and its request body has no generally defined semantics. &lt;code&gt;QUERY&lt;/code&gt; asks the target resource to process the enclosed content safely and idempotently, then return the result.&lt;/p&gt;
&lt;p&gt;In other words, it carries structured content like &lt;code&gt;POST&lt;/code&gt;, while providing the safety and idempotency guarantees associated with &lt;code&gt;GET&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;QUERY /products/search HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Host: api.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Accept: application/json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;categories&amp;#34;: [&amp;#34;books&amp;#34;, &amp;#34;electronics&amp;#34;],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;price&amp;#34;: { &amp;#34;min&amp;#34;: 20, &amp;#34;max&amp;#34;: 200 },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;available&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;sort&amp;#34;: [{ &amp;#34;field&amp;#34;: &amp;#34;rating&amp;#34;, &amp;#34;direction&amp;#34;: &amp;#34;desc&amp;#34; }]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="why-not-just-use-get"&gt;Why Not Just Use GET?&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; works perfectly for small filters:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;GET /products?category=books&amp;amp;available=true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But complex queries quickly become problematic. URLs have practical length limits across clients, proxies, gateways, and servers. Nested conditions are awkward to encode, percent-encoding hurts readability, and URLs commonly appear in browser history, access logs, analytics tools, and referrer data.&lt;/p&gt;
&lt;p&gt;Using a body solves the serialization problem, but a body on &lt;code&gt;GET&lt;/code&gt; is not interoperable. Some implementations reject it, others ignore it, and HTTP does not define what it means.&lt;/p&gt;
&lt;h3 id="why-not-just-use-post"&gt;Why Not Just Use POST?&lt;/h3&gt;
&lt;p&gt;Many APIs already use &lt;code&gt;POST /search&lt;/code&gt; or &lt;code&gt;POST /query&lt;/code&gt;. This works, but it hides the operation&amp;rsquo;s intent from generic HTTP infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; is potentially state-changing and potentially non-idempotent. Developers may know that a particular endpoint is read-only, but proxies, caches, retry mechanisms, and generic clients cannot infer that from the method.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; makes the intent explicit.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;QUERY&lt;/th&gt;
&lt;th&gt;POST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Safe&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not guaranteed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotent&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not guaranteed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request body semantics&lt;/td&gt;
&lt;td&gt;Not defined&lt;/td&gt;
&lt;td&gt;Expected&lt;/td&gt;
&lt;td&gt;Defined by the resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cacheable response&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large structured input&lt;/td&gt;
&lt;td&gt;Poor fit&lt;/td&gt;
&lt;td&gt;Good fit&lt;/td&gt;
&lt;td&gt;Good fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem support&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="safe-and-idempotent"&gt;Safe and Idempotent&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; is registered by IANA as both safe and idempotent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Safe&lt;/strong&gt; means the client is not asking to change the target resource. The server may still write logs, update metrics, or populate a cache, just as it can for &lt;code&gt;GET&lt;/code&gt;. However, an operation that sends an email, charges a card, or changes business data must not use &lt;code&gt;QUERY&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idempotent&lt;/strong&gt; means the request can be repeated without requesting additional state changes. A client or gateway can therefore retry it after a connection failure.&lt;/p&gt;
&lt;p&gt;This does not mean every response will contain identical data. Results may change over time. It means that repeating the request does not repeat a mutation.&lt;/p&gt;
&lt;h3 id="where-query-fits"&gt;Where QUERY Fits&lt;/h3&gt;
&lt;p&gt;The method is useful for read-only operations with rich inputs, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complex search with nested filters, facets, and ranking controls&lt;/li&gt;
&lt;li&gt;Analytics with dimensions, metrics, grouping, and time ranges&lt;/li&gt;
&lt;li&gt;Geospatial queries containing polygons or GeoJSON&lt;/li&gt;
&lt;li&gt;Graph and relationship traversal&lt;/li&gt;
&lt;li&gt;Batch retrieval of hundreds of IDs&lt;/li&gt;
&lt;li&gt;Query languages such as JSONPath, SPARQL, or a domain-specific language&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The request&amp;rsquo;s &lt;code&gt;Content-Type&lt;/code&gt; identifies the query format. A server can also advertise supported formats with the new &lt;code&gt;Accept-Query&lt;/code&gt; response header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;204&lt;/span&gt; &lt;span class="ne"&gt;No Content&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Allow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;GET, HEAD, QUERY, OPTIONS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Accept-Query&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="l"&gt;application/json, application/jsonpath&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="the-trade-offs"&gt;The Trade-offs&lt;/h3&gt;
&lt;p&gt;Standardized does not mean universally supported.&lt;/p&gt;
&lt;p&gt;Some reverse proxies, API gateways, WAFs, CDNs, frameworks, and generated clients may reject an unfamiliar method or fail to apply its retry and caching semantics correctly. The complete path from client to origin must be tested.&lt;/p&gt;
&lt;p&gt;Cross-origin browser requests also require a CORS preflight because &lt;code&gt;QUERY&lt;/code&gt; is not a safelisted method.&lt;/p&gt;
&lt;p&gt;Caching is possible, but more complex than with &lt;code&gt;GET&lt;/code&gt;. The cache key must include the request body and relevant metadata, not only the URI. Incorrect body normalization could return the wrong result.&lt;/p&gt;
&lt;p&gt;Moving query parameters into the body also reduces accidental exposure through URL-focused logs, but it does not make the data secret. Applications, gateways, and tracing systems may still log request bodies.&lt;/p&gt;
&lt;p&gt;Finally, a &lt;code&gt;QUERY&lt;/code&gt; is not directly bookmarkable. A server can solve this by returning a &lt;code&gt;Location&lt;/code&gt; or &lt;code&gt;Content-Location&lt;/code&gt; URI that clients can later access with &lt;code&gt;GET&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="should-you-use-it-today"&gt;Should You Use It Today?&lt;/h3&gt;
&lt;p&gt;Keep using &lt;code&gt;GET&lt;/code&gt; when the query is small, naturally fits in a URI, and benefits from links, bookmarks, and universal caching.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;QUERY&lt;/code&gt; when the operation is genuinely safe and idempotent, its input is too complex for a URI, and you control or have tested the relevant infrastructure.&lt;/p&gt;
&lt;p&gt;Keep using &lt;code&gt;POST&lt;/code&gt; for mutations and when compatibility is more important than expressing read-only semantics at the protocol level.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QUERY&lt;/code&gt; is not simply a new syntax. It gives a precise HTTP meaning to operations that have been awkwardly living behind &lt;code&gt;POST&lt;/code&gt; for years.&lt;/p&gt;
&lt;p&gt;For simple filters, &lt;code&gt;GET&lt;/code&gt; remains the best choice. For mutations, use &lt;code&gt;POST&lt;/code&gt; or another appropriate method. For large, structured, read-only operations, HTTP finally has an honest method name.&lt;/p&gt;
&lt;h3 id="references"&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>