"HTTP/1.1 needs to know either how long a response body is (Content-Length) or that the body ends when the connection closes (HTTP/1.0 style). Chunked transfer is the third option: a self-delimiting body of unknown final size. It's what makes streaming possible without keeping a connection open forever."
The Length Problem
HTTP/1.1 needs to know when one response ends so it can start reading the next (keep-alive connection reuse). Two options for fixed-size bodies:
- Content-Length — send the exact byte count as a header. Client reads N bytes, knows the response is done.
- Connection: close — body ends when the server closes the connection. Defeats keep-alive.
Neither works when the body is generated on the fly and the final size isn't known until done. AI streaming, log tailing, large query results — all need to start writing before knowing the total. That's what Transfer-Encoding: chunked solves.
The Chunked Format
Each chunk is preceded by its size in hex, then CRLF, then the chunk bytes, then CRLF. The end is signaled by a chunk of size 0:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
2A\r\nevent: message\ndata: {"content":"Hi"}\n\n\r\n
2C\r\nevent: message\ndata: {"content":"아빠"}\n\n\r\n
0\r\n\r\n
2A hex = 42 bytes of chunk content. 2C hex = 44 bytes. 0 with the final CRLF marks the end. Most clients and servers handle this transparently — you write your async generator's yield, the HTTP library frames it as chunks.
What Chunked Enables
- SSE — body is a stream of events of unknown count and size.
- Large file downloads — server can start sending before computing the total size.
- Real-time logs — server tails a file and writes chunks as new lines arrive.
- Pipelined query results — DB streams rows; server writes each row as a chunk.
HTTP/2 and HTTP/3 don't expose chunked encoding explicitly — they have their own binary framing — but the semantic is identical: a streaming body of unknown total length.
The Gotchas
1. Trailers. Chunked responses can include headers AFTER the body, declared by Trailer: in the leading headers. Useful for HMAC signatures over streamed content. Rarely supported by intermediaries; use with caution.
2. Buffering breaks streaming. Same gotcha as SSE — proxies that buffer chunked responses collect everything before forwarding, defeating the streaming. Always set X-Accel-Buffering: no or equivalent for streaming responses.
3. Don't set Content-Length on chunked responses. They're mutually exclusive. Some old servers do both; some old clients get confused.