Content-Length content both
The size of the message body in bytes. Lets the recipient know exactly how much data to read.
What it does
Content-Length tells the recipient the exact size of the message body in bytes. The recipient uses it to know when the body ends — without it, the only other mechanisms for framing a body are chunked transfer encoding or closing the connection.
It works in both directions. A server sends it with responses that have a body. A client sends it with requests that have a body (POST, PUT, PATCH).
Syntax
Content-Length: <length>
The value is a non-negative decimal integer representing the number of bytes in the body after any transfer encoding is removed but before any content encoding is applied.
Examples:
Content-Length: 0
Content-Length: 348
Content-Length: 1048576
When it's required, optional, or forbidden
Required:
- On responses to HEAD requests — the value should reflect what the body would be on a GET, even though no body is sent
- On responses where the body length is known in advance and
Transfer-Encodingis not used
Optional (can omit):
- On responses where the body is dynamically generated and length isn't known upfront — use chunked
Transfer-Encodinginstead - On 1xx, 204, and 304 responses, which have no body
Forbidden:
- You cannot use both
Content-LengthandTransfer-Encoding: chunkedon the same message.Transfer-Encodingtakes precedence if both appear — some intermediaries will stripContent-Lengthin this case.
How it interacts with other headers
Content-Length and Transfer-Encoding are mutually exclusive framing mechanisms. Content-Length says "the body is exactly N bytes." Transfer-Encoding: chunked says "the body will arrive in chunks and you'll know it's done when you see a zero-length chunk." HTTP/1.1 added chunked encoding specifically for cases where the server doesn't know the final size before it starts sending.
For range requests, Content-Length reflects the length of the partial body being returned (the range), while Content-Range describes where that slice sits within the full resource.
Common mistakes and gotchas
Sending the wrong byte count. If Content-Length is shorter than the actual body, the client stops reading mid-body and silently truncates the data. If it's longer, the client hangs waiting for bytes that never arrive, eventually timing out. Both are silent failures that are annoying to debug.
Forgetting to account for encoding. Content-Length must reflect the byte count of the encoded body as it appears on the wire — not the original string length in your application language. A string that's 100 characters in JavaScript might be 120+ bytes when serialized to UTF-8 JSON. Always measure bytes, not characters.
Setting Content-Length on chunked responses. Streaming responses (server-sent events, large file downloads, real-time data) use chunked encoding precisely because the size isn't known upfront. Setting a Content-Length on a stream that isn't fully buffered will either be wrong or force your server to buffer the entire thing before sending — defeating the purpose.
Compressing without updating Content-Length. If your application sets Content-Length and then gzip middleware runs after, the compressed body will be a different size. The fix: either don't set Content-Length and let the framework handle it, or ensure compression runs before you calculate the length.
Real-world examples
Simple JSON response:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
{"status":"ok","message":"User created"}
POST request with a body:
POST /api/users HTTP/1.1
Content-Type: application/json
Content-Length: 51
{"name":"Sainesh","email":"[email protected]"}
Response with no body (204):
HTTP/1.1 204 No Content
No Content-Length needed — 204 by definition has no body.
HEAD response (length declared, no body sent):
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 204800
The body is not sent, but Content-Length still reflects what a GET would return — useful for clients that want to know file size before downloading.
FAQ
Is Content-Length required for POST requests?
Technically no — HTTP/1.1 allows chunked transfer encoding on requests too, and HTTP/2 has its own framing. But in practice, most server frameworks and APIs expect Content-Length on request bodies unless you're explicitly streaming. Many reverse proxies also buffer the request body and require a known length. Unless you're doing streaming uploads, always send Content-Length on request bodies.
What happens if Content-Length is 0?
It means the body is empty — zero bytes. This is valid and common on responses like 201 Created where the server just confirms the action without returning data. It's also how you explicitly signal "there is a body, but it's empty" — different from omitting the header entirely.
Can Content-Length ever be negative?
No. A negative value is invalid per the spec. Some implementations will reject the request or response outright; others might treat it as zero or ignore it.
Why do HTTP/2 and HTTP/3 not need Content-Length as much?
HTTP/2 and HTTP/3 use binary framing with explicit length fields in each frame, so the protocol itself handles body framing. Content-Length is still valid and often sent (for compatibility and to allow early size checks), but it's no longer the primary mechanism for determining when the body ends.
Fun fact
The Content-Length header has existed since HTTP/1.0 in 1996, but persistent connections in HTTP/1.1 made it genuinely critical. In HTTP/1.0, servers would signal end-of-body by closing the connection — which meant a new TCP connection for every resource. HTTP/1.1's keep-alive connections needed a different framing mechanism, making Content-Length (and later chunked encoding) essential to the modern web's performance.