RFC 9110: The Ambiguous GET Request Body - Beyond the "No Defined Semantics" Trap

RFC 9110: The Ambiguous GET Request Body - Beyond the "No Defined Semantics" Trap
TL;DR
RFC 9110, the latest HTTP semantics specification, explicitly states that the GET request method's payload has "no generally defined semantics." This means while technically possible to send data in a GET request, it's an anti-pattern that breaks expectations, can lead to unpredictable behavior across intermediaries, and is often ignored by servers. Understanding this RFC nuance is crucial for building robust and secure web applications, avoiding potential vulnerabilities, and ensuring predictable network traffic analysis.
The GET Method: A Quick Refresher (and a Warning)
The Hypertext Transfer Protocol (HTTP) is the backbone of data communication on the World Wide Web. Its methods (verbs) define the intended action on a resource. GET is designed to retrieve a representation of a specified resource. Think of it as asking a server for information.
Historically, and as reinforced by RFC 9110, GET requests are intended to be:
- Idempotent: Making the same
GETrequest multiple times should have the same effect as making it once. - Safe:
GETrequests should not cause any side effects on the server (i.e., they shouldn't change the server's state).
The RFC 9110 section 4.3.1, "GET," states:
The GET method requests a representation of the specified resource. Requests using GET should only retrieve data and should have no other side effects. [...] The GET method is not intended to be used with a request body.
Crucially, it continues:
The presence of a payload body on a GET request is not defined by this specification and is not recommended.
This is the core of the "no generally defined semantics." While the RFC doesn't forbid sending a body with GET, it explicitly states there are no defined semantics for it. This ambiguity is a breeding ground for issues.
Why Sending a Body with GET is a Bad Idea (and What Happens)
Imagine a scenario where you're building a web service and decide to send some parameters for a GET request in the body, perhaps for convenience or due to a misunderstanding.
Example Scenario: A Misguided API Design
Let's say you have an API endpoint /users and you want to filter users by status and role. A "clever" developer might think:
GET /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"status": "active",
"role": "admin"
}What's Wrong With This?
Server-Side Ambiguity: A server receiving this request might:
- Ignore the body entirely: Many web frameworks and servers are designed to disregard the body of
GETrequests, assuming it's invalid. Your filtering parameters would be lost. - Treat it as an error: The server might return a
400 Bad Requestor405 Method Not Allowedbecause it doesn't expect a body forGET. - Process it unexpectedly: In rare, poorly implemented cases, a server might try to parse the body, leading to inconsistent behavior or even security vulnerabilities if the parsing is flawed.
- Ignore the body entirely: Many web frameworks and servers are designed to disregard the body of
Proxy/Cache Issues: Intermediary systems like proxies and caches are designed to optimize HTTP traffic. They often cache responses based on the request URL. If a
GETrequest unexpectedly includes a body, these intermediaries might not cache it correctly, or worse, might return cached content from a differentGETrequest that happened to have the same URL but no body. This can lead to stale or incorrect data being served.Security Implications: While not a direct exploit like a SQL injection, this ambiguity can be leveraged. An attacker might craft a
GETrequest with a malicious payload in the body, hoping a vulnerable server or intermediary will process it in an unintended way. This could potentially lead to denial-of-service conditions or, in very specific circumstances, information disclosure if the server mishandles the unexpected data.
Practical Example: Using curl to Demonstrate
Let's try sending a GET request with a body using curl.
# This command sends a GET request with a JSON body.
# Most servers will ignore the body.
curl -v -X GET \
-H "Content-Type: application/json" \
--data '{"filter": "active"}' \
http://httpbin.org/get
# Expected Output Snippet (from httpbin.org, which is designed to echo requests):
# < HTTP/1.1 200 OK
# ...
# {
# "args": {}, <-- Notice 'args' is empty, indicating the body was not parsed as query parameters.
# "data": "{\"filter\": \"active\"}", <-- The body is echoed under 'data'.
# "files": {},
# "form": {},
# "headers": {
# "Accept": "*/*",
# "Content-Type": "application/json",
# "Host": "httpbin.org",
# "User-Agent": "curl/7.81.0",
# "X-Amzn-Trace-Id": "Root=..."
# },
# "json": null, <-- 'json' is null, meaning it wasn't parsed as JSON.
# "origin": "...",
# "url": "http://httpbin.org/get"
# }As you can see from httpbin.org (a service designed for testing HTTP requests), the GET request was received, but the args and json fields are empty. The body was treated as raw data, not as query parameters or JSON payload to be interpreted by the server's routing logic for GET.
The Correct Way: Using Query Parameters or POST
If you need to send parameters to a server, especially for filtering or complex data, the correct HTTP methods are:
GETwith Query Parameters: For simple filtering or identifiers.GET /users?status=active&role=admin HTTP/1.1 Host: api.example.comThis is universally understood and handled by servers and intermediaries.
POSTwith a Request Body: For sending complex data, creating new resources, or when the operation is not idempotent or safe.POST /users/filter HTTP/1.1 Host: api.example.com Content-Type: application/json { "status": "active", "role": "admin" }This clearly signals that data is being sent to the server for processing, and the server is expected to handle it.
Practical Implications for Security Professionals
Traffic Analysis and IOCs: When analyzing network traffic (e.g., using Wireshark), be aware that
GETrequests with bodies are anomalous. If you see them, investigate. They could indicate misconfigurations, poorly designed applications, or potentially malicious activity attempting to hide data or exploit parsing logic. Indicators of Compromise (IOCs) might include unusualGETrequests with non-standard payloads.Web Application Security Testing: During penetration testing, intentionally sending
GETrequests with bodies to an application can reveal vulnerabilities. For example:- Does the server correctly reject these requests?
- Does it mishandle the data in the body, perhaps leading to unexpected behavior or information leakage?
- Are there any caching issues introduced by these non-standard requests?
Zero-Day Hunting: While not a direct "zero-day" in the traditional sense of a software flaw, exploiting the ambiguity of RFC 9110 regarding
GETbodies could be a vector. If a server or proxy implements custom logic forGETbodies that is not robust, it could be a novel attack surface.Defensive Coding: Developers must adhere to RFC semantics. Never send a body with
GET. If you need to pass data, use query parameters forGETor switch toPOST. This prevents a class of potential misinterpretations and vulnerabilities.
Quick Checklist
- Understand RFC 9110: Know that
GETrequests should not have a body. - Avoid
GETwith Body: Never design APIs or applications that send data in theGETrequest body. - Use Query Parameters: For filtering or passing simple data with
GET. - Use
POST: For sending complex data or when side effects are intended. - Monitor Network Traffic: Treat
GETrequests with bodies as suspicious. - Test Application Robustness: During security assessments, probe how applications handle non-standard
GETrequests.
References
- RFC 9110: HTTP Semantics: https://datatracker.ietf.org/doc/html/rfc9110
- MDN Web Docs - HTTP Request Methods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
Source Query
- Query: rfc 9110 get request content has no generally defined semantics
- Clicks: 0
- Impressions: 47
- Generated at: 2026-04-29T20:28:31.419Z
