---
title: "Can a GET Request Have a Body?"
url: https://apyhub.com/blog/can-a-get-request-have-a-body
author: ApyHub
published: 2026-09-09T08:29:31.88643Z
---

# Can a GET Request Have a Body?

# Can a GET Request Have a Body?

**Technically yes. Practically no.**

HTTP does not forbid a body on a GET request. But RFC 9110 states that content in a GET request has **no generally defined semantics**, cannot change the meaning or target of the request, and may cause some implementations to reject the request and close the connection.

So you can send one. Nothing is obliged to read it, and some things will actively break.

## What the Spec Actually Says

RFC 9110 section 9.3.1 is the relevant text. Three points come out of it:

**Message framing is independent of the method.** A GET request can carry content the same way any request can. There is no rule against it at the protocol level.

**The content has no defined meaning.** A server receiving a body on a GET has no specification telling it what to do with that body. Ignoring it is correct behaviour.

**Implementations may reject it.** The spec explicitly warns that sending content on a GET might cause the request to be rejected and the connection closed.

That last point is why "technically allowed" is not the same as "will work."

**Worth noting the contrast with DELETE.** RFC 9110 is stricter there: it says a client SHOULD NOT generate content in a DELETE request, which is an explicit recommendation against. GET gets no such wording. So a GET body is undefined, while a DELETE body is actively discouraged. We covered that case separately in [Can HTTP DELETE Have a Body?](https://apyhub.com/blog/does-http-delete-have-body).""

## What Actually Breaks

Even where the spec permits it, the stack between you and the server often does not.

1. **Proxies and CDNs may strip the body.** Intermediaries are not required to forward content on a GET, and many drop it silently. Your request arrives without the data.
2. **Caches ignore it.** HTTP caching keys on the method and URL. Two GETs to the same URL with different bodies look identical to a cache, so you get the wrong cached response.
3. **Some servers reject the request outright.** Certain HTTP libraries and server configurations return an error rather than accept content on a GET.
4. **Client libraries silently drop it.** Several HTTP clients, including some browser fetch implementations, will not send a body on GET at all. Your code looks correct and the data never leaves.
5. **Logging and debugging get harder.** Tooling built around the assumption that GET has no body will not show you what was sent.

None of these are bugs. They are reasonable implementations of a method whose content has no defined semantics.

## Why People Want To

The usual reason is a complex query. A search endpoint with fifteen filters, nested conditions, and an array of IDs does not fit comfortably in a query string, and URLs have practical length limits around 2,000 characters in some browsers and proxies.

That is a real problem. The body is not the solution.

## What To Do Instead

**Use POST for complex queries.** It is the pragmatic answer and it is what most large APIs do. Elasticsearch, GraphQL, and many search APIs accept a POST with a JSON body for queries. You lose caching and semantic purity, and you gain a request that works everywhere.

**Encode the query in the URL.** Base64-encode a JSON filter object into a single query parameter. Ugly, cacheable, and works with every intermediary.

**Create a saved-query resource.** POST the query once, get an ID back, then GET the results by ID. More work, and it makes the query itself cacheable and shareable.

**Use QUERY, eventually.** A new HTTP method called QUERY has been proposed specifically for safe, idempotent requests that need a body. It is not widely available yet, so it is not a solution today.

## A Worked Example

You want to search products with several filters.

**What people try:**

http

```
GET /products/search HTTP/1.1
Content-Type: application/json

{"category": "audio", "price_max": 200, "tags": ["wireless", "anc"]}
```

This may work in curl, work in Postman, and then fail in production behind a CDN that strips the body.

**What works:**

http

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

{"category": "audio", "price_max": 200, "tags": ["wireless", "anc"]}
```

Same request, a method whose body has defined semantics, and no intermediary will discard it.

## FAQ

### Is a GET request with a body valid HTTP?

Yes, it is not forbidden. RFC 9110 permits the framing but states the content has no generally defined semantics and warns that implementations may reject the request. Valid and reliable are different things.

### Will a server read the body of a GET request?

Only if it was specifically built to. There is no specification telling a server what a GET body means, so ignoring it is correct behaviour. Frameworks vary and most do not parse it by default.

### Why does my GET body work in Postman but not in production?

Postman sends it and your local server may read it. Between your client and the server in production sit proxies, load balancers and CDNs, any of which may drop the content. Test through the full stack, not just against localhost.

### Should I use POST instead of GET for a complex search?

Usually yes. You lose HTTP caching and the semantic signal that the request is safe, and you get something that works through every intermediary. Most large search APIs made this trade.

### Can a DELETE request have a body?

Also technically yes, and the spec is stricter about it. RFC 9110 says a client SHOULD NOT generate content in a DELETE request, which is stronger wording than it uses for GET. Put identifiers in the URL path and filters in query parameters instead. [Full answer here](https://apyhub.com/blog/does-http-delete-have-body).

### What is the QUERY method?

A proposed HTTP method for safe, idempotent requests that carry a body, designed to solve exactly this problem. It is not broadly supported yet, so it is not something to build on today.

### Does GraphQL use GET or POST?

Almost always POST, for this reason. GraphQL queries are frequently too large or too structured for a query string, so the body carries them and POST gives the body defined meaning.

## Related

* [Can HTTP DELETE Have a Body?](https://apyhub.com/blog/does-http-delete-have-body) - the same question for DELETE, where the spec is stricter
* [PUT vs PATCH](https://apyhub.com/blog/put-vs-patch-which-one-should-you-use) - two methods whose bodies do have defined meaning
* [405 Method Not Allowed](https://apyhub.com/blog/405-method-not-allowed) - when the verb is wrong for the resource
* [Stateful vs Stateless Protocols](https://apyhub.com/blog/stateful-vs-stateless-protocols-the-difference-and-why-rest-is-stateless) - why REST is strict about methods
* [API Fundamentals](https://apyhub.com/blog/api-fundamentals-what-an-api-is-and-how-to-use-one-without-writing-code) - methods, requests and responses from scratch

Source: [RFC 9110, section 9.3.1](https://www.rfc-editor.org/rfc/rfc9110.html)

***

## About ApyHub

[ApyHub](https://apyhub.com/) is a curated API catalog for developers, teams and AI agents: [file conversion](https://apyhub.com/catalog/file-conversion), [data validation](https://apyhub.com/catalog/data-validation), [OCR and extraction](https://apyhub.com/catalog/artificial-intelligence) and more across 20 categories. One key covers all of it, every endpoint is [MCP-ready](https://apyhub.com/mcp) so AI agents can discover and call them directly, and every service page has a playground for testing before you build.

EU-based and EU-hosted, which keeps data residency simple for teams with GDPR obligations.

[**Browse the catalog**](https://apyhub.com/catalog) | [**Get a free API key**](https://apyhub.com/auth/signup) - no credit card required.
