Skip to main content

QUERY: We've been abusing POST for 20 years

In June 2026, a new HTTP request method, or verb, was introduced. RFC 10008 describes Query as:

A QUERY requests that the request target process the enclosed content in a safe and idempotent manner and then respond with the result of that processing. This is similar to POST requests, but QUERY requests can be automatically repeated or restarted without concern for partial state changes.

But what does that mean and why do we need it?

What’s Wrong with GET?

When we want to retrieve a resource from a RESTful interface, we use GET. It’s simple. It’s easy to use. A GET request has no defined request body, and it should be idempotent. That means we can make the same request over and over again without changing any state in the service. The response itself might be different if the resource has changed, but the GET request shouldn't be the thing that causes that change.

Sometimes we need to add query parameters to a GET request. Perhaps we want to specify the maximum number of resources to return, request the third page of results, or filter the response so we only get the green ones. As the request becomes more complex, the target URI (Uniform Resource Identifier) can become very large. The HTTP specification doesn't define a maximum URI length, but browsers, proxies, web servers and frameworks all impose practical limits.

Encoding increasingly complex data into the URI can also become cumbersome. Worse still, the data may be sensitive, and including it in the URI increases the risk of it being logged by clients, proxies or servers along the way.

Suddenly, GET is no longer the ideal choice. The operation is still a safe, idempotent retrieval of resources, but expressing the request as a URI is becoming awkward, inefficient and potentially risky.

Why Not POST?

When GET is no longer the ideal choice, perhaps because the URI has become cumbersome or needs to convey sensitive data, you may find yourself turning to POST. In fact, until now, POST has been the only real alternative. It has a request body with no maximum size defined by the HTTP specification and, when used over HTTPS, is protected by TLS encryption.

However, POST isn't a retrieval method. It's a general purpose method that is commonly used to create resources, and it isn't defined as safe or idempotent. In other words, a POST request may change the state of the service, and making the same request multiple times isn't guaranteed to have the same effect. If all you're trying to do is retrieve resources, POST doesn't accurately describe your intent.

This leaves us in an awkward position. GET has the semantics we want for retrieving resources, but the URI may no longer be the best place to express a complex query. POST gives us a request body, but it doesn't accurately describe our intent. We aren't trying to create a resource or trigger an action; we are still trying to retrieve data.

Enter Query

The shortcomings of GET, and the way POST has been stretched beyond its original intent to work around them, are why we have QUERY. It gives us the ability to send a request body while retaining the semantics we need for resource retrieval: it is safe and idempotent. In fact, QUERY is designed specifically for read only operations that need a request body. As intermediaries understand that QUERY does not modify server state, they can safely apply behaviours such as retries and caching, where supported.

The rule of thumb is:

  • Use GET when the request can naturally be expressed by the URI.
  • Use QUERY when the request can no longer be naturally or practically expressed by the URI.

This makes QUERY a natural fit for:

  • Complex search filters.
  • Reporting endpoints.
  • Large or nested JSON queries.
  • Any read-only operation where the request would otherwise be awkward to squeeze into a URL.

The RFC became an Internet Standards Track RFC in June 2026. It will likely take time for browsers, frameworks, API gateways, proxies and client libraries to add first-class support, so don't expect QUERY to replace POST overnight. I was pleased to find that ExpressJS already supports QUERY. However, I was disappointed to find that the Firefox browser doesn't support it yet.

QUERY will not replace GET in the browser address bar, for hyperlinks or HTML form methods. I think this is a shame, as I can see value in using the QUERY body to pass data that would otherwise have to be encoded as query parameters when navigating from one page to another.

Finally

We've been abusing POST for 20 years, and now HTTP finally has a verb that says what we actually mean. QUERY doesn't introduce a new capability, it standardises a pattern software engineers have been using for years. It finally lets APIs express the intent: "This is a query, not an update." 

QUERY removes a discomfort I have felt keenly, and I can’t wait to start using it.

Comments

Popular posts from this blog

It's great to be back at the ACCU Conference 2025!

Last week I was back at the ACCU Conference in Bristol, for the first time since I gave an opening keynote in 2019 . In March 2020, COVID hit the UK and I got out of the habit of attending and speaking at conferences. Plus the ACCU Conference, and the organisation in general, took a big step back towards C++ and that hasn’t been relevant to me for nearly two decades. It’s taken until now, 2025 for me to want to talk again, and I was really pleased to be accepted for the ACCU Conference again. It was just a 20 minute session and I only attended for the day I was speaking. There was enough varied content for it to be interesting to me and I’m hoping to be back for the full conference next year. I really enjoyed attending and it was great to catch up with people I hadn’t seen for years. I felt re-engaged and particularly liked being asked if I’d been to nor(DEV):con . Learning to stop writing code (and why you won't miss it) Daisy Hollman I should have read the summary of the openin...

Write Your Own Load Balancer: A worked Example

I was out walking with a techie friend of mine I’d not seen for a while and he asked me if I’d written anything recently. I hadn’t, other than an article on data sharing a few months before and I realised I was missing it. Well, not the writing itself, but the end result. In the last few weeks, another friend of mine, John Cricket , has been setting weekly code challenges via linkedin and his new website, https://codingchallenges.fyi/ . They were all quite interesting, but one in particular on writing load balancers appealed, so I thought I’d kill two birds with one stone and write up a worked example. You’ll find my worked example below. The challenge itself is italics and voice is that of John Crickets. The Coding Challenge https://codingchallenges.fyi/challenges/challenge-load-balancer/ Write Your Own Load Balancer This challenge is to build your own application layer load balancer. A load balancer sits in front of a group of servers and routes client requests across all of the serv...

Catalina-Ant for Tomcat 7

I recently upgraded from Tomcat 6 to Tomcat 7 and all of my Ant deployment scripts stopped working. I eventually worked out why and made the necessary changes, but there doesn’t seem to be a complete description of how to use Catalina-Ant for Tomcat 7 on the web so I thought I'd write one. To start with, make sure Tomcat manager is configured for use by Catalina-Ant. Make sure that manager-script is included in the roles for one of the users in TOMCAT_HOME/conf/tomcat-users.xml . For example: <tomcat-users> <user name="admin" password="s3cr£t" roles="manager-gui, manager-script "/> </tomcat-users> Catalina-Ant for Tomcat 6 was encapsulated within a single JAR file. Catalina-Ant for Tomcat 7 requires four JAR files. One from TOMCAT_HOME/bin : tomcat-juli.jar and three from TOMCAT_HOME/lib: catalina-ant.jar tomcat-coyote.jar tomcat-util.jar There are at least three ways of making the JARs available to Ant: Copy the JARs into th...