1. Overview
In this tutorial, we’ll learn why an HTTP GET request shouldn’t be sent with a body.
First, we’ll see why that request is usually used**.** Then**,** we’ll look into the consequences of sending it. Finally, we’ll explore the alternatives to having a body in a GET request.
2. Why Are Some GET Requests Sent With a Body?
When developing REST APIs, we may come across the need to attach data to the body of a GET request. That can happen when performing a complex search or if the URI is too long for the server to handle. It originated from an interpretation of the initial HTTP/1.1 specification. Then, it became convenient and widespread.
2.1. Unclear HTTP/1.1 Specification
The RFC 7231 defined HTTP semantics and content. Unfortunately, it didn’t clearly state what should happen to a GET request with a body:
A payload within a GET request message has no defined semantics.
That sentence was subject to many interpretations. It could mean that the semantics are left to each implementer of the specification. But it could also mean that there’s no semantic to such a request. Consequently, some HTTP servers allowed it. Others, like browsers, load balancers, and caching proxies, didn’t.
2.2. Convenience
We may want to send GET requests with bodies for convenience. First, because encoding a JSON payload can be easier than dealing with complex query strings. JSON is more structured and has built-in support in a large range of programming languages. That influenced some design decisions in the Elasticsearch empty_search API:
Instead of the cryptic query-string approach, a request body search allows us to write queries by using the query domain-specific language, or query DSL.
Secondly, if we control the client and server, it’s easier to send the data in the request body. We know that it’ll be received on the server.
Thirdly, it’s more aesthetic to send a GET with a body to retrieve data rather than POST. The latter seems counter-intuitive. That’s because POST requests are largely viewed as a means of creating resources. The authors of Elasticsearch have built their search API around that:
The authors of Elasticsearch prefer using GET for a search request because they feel that it describes the action—retrieving information—better than the POST verb. However, because GET with a request body is not universally supported, the search API also accepts POST requests:
3. Why Is Performing a GET With a Body a Bad Idea?
In most situations, having a GET request body offers better readability and a better developer experience (DX) over the query parameters. While technically possible to perform, that type of request should be highly avoided for many reasons.
3.1. RFC 9110 Compliance
In 2014, the RFC 7231 superseded HTTP/1.1. It added a warning against the use of GET with a body:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
In 2019, its successor, RFC 9110, tightened the language around GET with bodies:
A client SHOULD NOT generate content in a GET request unless it is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported. An origin server SHOULD NOT rely on private agreements to receive content, since participants in HTTP communication are often unaware of intermediaries along the request chain
That amendment was introduced to definitely clear the ambiguity raised by HTTP/1.1. It’s better not to send a body in a GET request. That guarantees total interoperability with all systems, regardless of the specification version.
3.2. Ignored Request Body
HTTP/1.1 initially recommended to ignore GET requests bodies:
if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request
Consequently, some old servers that implemented it won’t correctly process our request.
3.3. Rejected Request
Some HTTP implementations reject GET requests that have a request body. As a matter of fact, fetch API throws an error in such a scenario. Similarly, Spring throws HttpMessageNotReadableException when a body is passed.
3.4. Uncached Request
The RFC 9110 states that a GET request should be cacheable. That constraint is violated if we send a body because proxies won’t read that request body. As a result of uncached data, we’ll hit performance issues due to multiple server calls.
4. What Are the Alternatives to a GET With a Body?
Let’s explore a few alternatives to using GET request bodies. Some of them are not standardized yet.
4.1. HTTP POST
First, POST is a viable replacement to GET with body. Indeed, out of all the HTTP methods, POST has the least amount of limitations. We can always default to it. If the semantics of a request fits within its constraints, we should prefer to use a more specific method.
Ultimately, it’s always safe to use POST whenever we’re tempted to add a body to a GET request.
4.2. HTTP QUERY
Next, we have the QUERY method. It should help making a safe, cacheable, idempotent request that contains content. Specifically, it accepts a request payload that contains a query operation:
QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: example/query
Accept: text/csv
select surname, givenname, email limit 10
This request should return a successful response:
HTTP/1.1 200 OK
Content-Type: text/csv
surname, givenname, email
Smith, John, [email protected]
Jones, Sally, [email protected]
Dubois, Camille, [email protected]
The QUERY method is a promising proposal. Until it becomes an implemented specification, it’s unusable for the public.
4.3. HTTP SEARCH
Finally, we can use the non-standard SEARCH method. It’s similar to QUERY but accepts a body of JSON or XML type:
SEARCH /search HTTP/1.1
Host: www.example.com
Content-Type: application/json
{
"query": "baeldung",
"sort": "date",
"filters": {
"type": "article",
"category": "tech"
}
}
Each key of the SEARCH body is understandable by the server. All of them are used to perform the search and return appropriate content. The method is not widespread yet and is only available for WebDAV SEARCH specification.
5. Conclusion
In this article, we learned why we shouldn’t send HTTP GET requests with a body. We first saw that those requests originated from an interpretation of the initial HTTP 1.1 specification.
Next, we explored the many bad consequences they could cause. Finally, we’ve discussed various alternatives available.
Overall, it’s always a good idea to align with the most recent HTTP specification. It guarantees having interoperable software.