Chunked Transfer in Web Development — What It Is, Format, and the Principle of Chunked Transfer

Author: IT Sectr Published: 2026-03-10 Reading time: 9 min

Chunked Transfer is an HTTP protocol mechanism where the server transmits the response body in separate fragments (chunks) without specifying the total data size in advance. Each chunk contains its size in hexadecimal format and data of the specified length, ending with a final zero-size chunk. According to MDN Web Docs, 2025, Transfer-Encoding: chunked is automatically enabled by the server when the response size is unknown in advance — for example, during on-the-fly content generation or streaming data transfer.

Key Takeaways

  • Chunked Transfer — transmission of HTTP response in parts without prior specification of Content-Length.
  • Transfer-Encoding: chunked — the header that enables chunked data transfer mode.
  • Each chunk contains its size in hex, data, and a trailing CRLF, and the end is indicated by a zero-size chunk.
  • Streaming — the main use of chunked transfer for audio, video, and SSE events.
  • Chunked Transfer is incompatible with the Content-Length header — they are not used simultaneously.

What Is Chunked Transfer?

Chunked Transfer is an HTTP mechanism defined in the HTTP/1.1 specification (RFC 7230, Section 4.1) that allows the server to send the response body in parts without specifying the total Content-Length. Instead of calculating the response size before sending, the server starts transmission immediately, sending data fragments as they become ready. Each fragment is accompanied by its own size header, allowing the client to assemble the response from pieces.

The mechanism is enabled by the Transfer-Encoding: chunked header. When the client sees this header in the response, it knows that the body will be transmitted in chunks and must read the response in a loop: read the chunk size, then read data of the specified size, then repeat. The process ends when a zero-size chunk is encountered. Chunked Transfer is a mandatory part of HTTP/1.1, supported by all modern web servers and HTTP clients.

The main reason for using chunked transfer is dynamic content generation. When the server generates a response based on a database query, external API call, or long computation, it cannot know the result size in advance. Instead of buffering the entire response in memory (which is risky for large volumes), the server enables Transfer-Encoding: chunked and sends data as it becomes available. This is especially important for servers with limited memory and for responses whose size can be very large — from 100 MB and above.

Difference Between HTTP/1.1 Chunked and HTTP/2

In HTTP/2, the chunked transfer mechanism as such does not exist because the protocol uses stream multiplexing at the frame level. In HTTP/2, data of any size is transmitted in DATA frames, and the response body size does not need to be declared in advance — a stream can be closed at any moment. Modern servers automatically convert HTTP/1.1 chunked responses to equivalent streaming transmission when proxying to an HTTP/2 upstream. Chunked Transfer remains relevant for HTTP/1.1 connections.

How Chunked Transfer Works

When the server decides to use Chunked Transfer, it does not calculate Content-Length but sends the Transfer-Encoding: chunked header. The response body is then formed as a sequence of chunks. Each chunk begins with a line containing the chunk size in hexadecimal format (without the 0x prefix), followed by CRLF ( ). Then comes the chunk data of the specified size, ending with CRLF. The last chunk has size 0, after which trailer headers may follow.

The hexadecimal size allows chunks of any size from 1 byte to theoretically unlimited volume to be transmitted. In practice, the chunk size is chosen by the server: typical values are 4 KB, 8 KB, or 16 KB. The optimal chunk size is a multiple of the TCP segment size (usually 1460 bytes for Ethernet) to minimize fragmentation at the transport layer. Nginx uses 4 KB chunks by default, Apache uses 8 KB chunks.

A client that receives Transfer-Encoding: chunked must read the response chunk by chunk until the terminating zero chunk. If the client does not support chunked transfer, the server cannot use this mode. In practice, all modern HTTP clients — browsers, OkHttp, URLSession, curl — fully support chunked responses. Streaming reads allow the client to start processing data before receiving the full response, which is critical for performance.

Chunk ElementFormatExample
Chunk SizeHEX + CRLF1000
Chunk Data[size bytes] + CRLF[4096 bytes of data]
Terminating Chunk0 0
Trailer (optional)Headers + CRLFExpires: Wed, 21 Oct 2025

Trailer Headers in Chunked Transfer

Chunked Transfer supports trailer headers — additional HTTP headers that are transmitted after the last chunk. This is useful for metadata that becomes known only after response generation is complete: for example, Content-MD5 or X-Compression-Ratio. Trailer headers must be declared in the Trailer header: Trailer: Content-MD5, X-Compression-Ratio. In practice, trailers are rarely used — most servers do not include them in responses.

Chunked Response Format

A chunked response has a strictly defined structure that the client must parse correctly. Let us consider a full example of an HTTP response with Transfer-Encoding: chunked. After the headers and an empty line, the response body begins. The body structure is a sequence of: chunk_size data chunk_size data ... up to 0 . Each size is transmitted in hexadecimal notation using ASCII characters.

Example server response with Chunked Transfer:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

7
Hello
6
World!
0

In this example, the server transmits the string "Hello World!" in two chunks. The first chunk is 7 bytes containing "Hello ", the second is 6 bytes containing "World!". The client collects data from both chunks and receives the full string. Important: the chunk size includes only the data, not the CRLF separators of the chunks themselves. The terminating empty chunk (0 ) notifies the client that the transmission is complete.

kotlin
import java.net.HttpURLConnection
import java.io.BufferedReader
import java.io.InputStreamReader

fun readChunkedResponse() {
    val url = java.net.URL("https://stream.example.com/data")
    val connection = url.openConnection() as HttpURLConnection
    val reader = BufferedReader(
        InputStreamReader(connection.inputStream)
    )

    var line: String?
    while (reader.readLine().also { line = it } != null) {
        println("Chunk: $line")
    }
    reader.close()
}

Parsing Chunked Response in OkHttp

OkHttp completely abstracts the developer from the details of Chunked Transfer. When receiving a response with Transfer-Encoding: chunked, OkHttp automatically collects the chunks and provides the developer with the full response body via response.body?.string(). For streaming processing, response.body?.source() is used, which returns a BufferedSource and allows reading data as it arrives. The developer does not need to manually parse hex sizes and CRLF — the library does this automatically.

Chunked Transfer vs Content-Length

Content-Length and Transfer-Encoding: chunked are two mutually exclusive ways to specify the size of an HTTP message body. Content-Length is a header that contains the exact body size in bytes. It is mandatory for responses whose size is known in advance and for requests with a body (POST, PUT). Content-Length allows the client to allocate a buffer of the required size in advance and verify that all data has been received.

Chunked Transfer is used when the body size is not known in advance. This occurs in three main scenarios: dynamic content generation (e.g., a database query whose result is not yet available), streaming large files (to avoid buffering the entire file in memory), and Server-Sent Events (SSE) for real-time event transmission. The choice between Content-Length and chunked is the server's responsibility. If the server knows the size before transmission begins, it should use Content-Length as a simpler and more predictable mechanism.

The HTTP/1.1 specification prohibits the simultaneous use of Content-Length and Transfer-Encoding: chunked. If the server sends both headers, the client must ignore Content-Length and process the response as chunked. The priority of Transfer-Encoding over Content-Length is established in RFC 7230 for cases where a proxy server modifies the response body and cannot preserve the original Content-Length. Some older HTTP clients handle this situation incorrectly, but modern implementations follow the specification.

When Content-Length Is Impossible

There are scenarios where Content-Length fundamentally cannot be calculated in advance. Dynamic reports generated on demand with filtering and aggregation — the server does not know the data volume until the database query completes. Streaming video transmitted from a camera in real time — the size is infinite. SSE and long polling for notifications — the response may last indefinitely. In all these cases, Chunked Transfer is the only correct mechanism.

Streaming Based on Chunked Transfer

Chunked Transfer underlies many streaming technologies on the web. The most well-known is Server-Sent Events (SSE), where the server sends events to the client over a single HTTP connection with Transfer-Encoding: chunked. SSE uses a special text format (data: message ), but the transport layer is ordinary chunked transfer. The browser receives events as the server sends them, without waiting for the response to complete.

Streaming audio and video also relies on Chunked Transfer. Media servers such as Nginx RTMP and Wowza Streaming Engine send media data in chunks over HTTP. The client-side player starts playback as soon as the first chunk is received, without waiting for the full file to load. This reduces the time to first frame from tens of seconds to 1-2 seconds. YouTube and Netflix use exactly this approach for their HTTP streams.

In mobile development, Chunked Transfer is used to transfer large volumes of data without loading the entire response into memory. When loading images via Coil or Glide on Android, libraries read streaming data chunk by chunk and gradually decode the image. This allows displaying large images (10+ MB) without OutOfMemoryError. OkHttp supports streaming reads via response.body?.byteStream(), which returns an InputStream that reads data chunk by chunk.

Chunked Transfer in gRPC and GraphQL

gRPC uses HTTP/2, where streaming is built into the protocol level and does not require a separate chunked mechanism. GraphQL servers running over HTTP/1.1 can use Chunked Transfer for streaming subscription results. Apollo Server and Hasura send chunked responses for GraphQL subscriptions, transmitting events as they occur. The client receives real-time updates without needing polling.

Advantages and Limitations

Chunked Transfer provides important advantages for web applications. Immediate data sending — the server does not buffer the response before sending, reducing latency to the first byte. Streaming processing — the client can start processing data as it arrives without waiting for full download. No memory limitations — the server does not store the full response in memory, which is critical for large data volumes. Ability to transmit infinite streams — SSE, live video, monitoring.

However, Chunked Transfer has limitations. Overhead for each chunk is 6-12 bytes for size + CRLF, which for many small chunks (e.g., 100 bytes each) can increase the response size by 10-15%. Inability to specify the exact size — the client cannot allocate a buffer in advance or show a progress bar. Issues with proxy servers — some older proxies do not support chunked transfer and cannot cache such responses. No support for resuming downloads — Range requests cannot be made for partially received chunked responses.

According to HTTP Archive, 2025, about 35% of all HTTP responses use Transfer-Encoding: chunked. Among them, dynamic pages predominate (60%), followed by API responses (25%) and media streams (15%). Static files almost always use Content-Length since their size is known in advance. The share of chunked responses is gradually decreasing with the adoption of HTTP/2, where streaming is implemented at the frame level without the need for an additional Transfer-Encoding header.

Practical Recommendations

In mobile development, use Chunked Transfer for downloading large files (images, video) and for API requests returning large data arrays. OkHttp fully supports chunked transfer without additional configuration. For uploads, Chunked Transfer is not used — HTTP/1.1 does not have Transfer-Encoding for uploads. On iOS, URLSession supports both sending and receiving chunked data without special configuration. Streaming JSON parsing (e.g., via Jackson Streaming API or Moshi) allows processing large JSON arrays as they arrive in a chunked stream.

Frequently Asked Questions

How does the server enable Chunked Transfer?

The server enables Chunked Transfer automatically when the response size is unknown. Nginx adds Transfer-Encoding: chunked if Content-Length is not set. In Spring Boot, StreamingResponseBody and SseEmitter automatically use chunked transfer. In Node.js Express, the response becomes chunked if res.write() and res.end() are called without Content-Length.

Can Content-Length and chunked be used simultaneously?

No, the HTTP/1.1 specification prohibits the simultaneous use of Content-Length and Transfer-Encoding: chunked. If the server sends both headers, the client must ignore Content-Length and process the response as chunked. This rule is established in RFC 7230 for compatibility with proxy servers that may modify the response body.

What is the optimal chunk size?

The optimal chunk size depends on the scenario. For ordinary web pages — 4-8 KB. For video streaming — 16-64 KB. For SSE — minimal chunks of 1-2 KB to reduce latency. The chunk size should be a multiple of the TCP segment size (1460 bytes for Ethernet) to minimize fragmentation at the transport layer.

Does Chunked Transfer work through proxies?

Modern proxy servers (Nginx, HAProxy, Envoy) support Chunked Transfer. The proxy can forward chunks without buffering (streaming) or buffer the entire response and resend it with Content-Length. Older proxies may buffer the chunked response until completion, increasing latency. HTTP/2 solves this problem at the protocol level.

How is Chunked Transfer different from HTTP chunked encoding?

They are the same thing. Chunked Transfer is the full name of the mechanism from the HTTP/1.1 specification. HTTP chunked encoding is the same, sometimes used in library documentation. Transfer-Encoding: chunked is the header that enables this mode. All three terms describe the same mechanism of transmitting data in parts.

Summary

  • Chunked Transfer — an HTTP/1.1 mechanism that transmits the response body in chunks without prior specification of Content-Length.
  • Transfer-Encoding: chunked — the header that enables chunked transfer; each chunk contains a hex size, data, and CRLF.
  • A terminating zero-size chunk signals the end of transmission, after which trailer headers may follow.
  • Dynamic content streaming — the main use case: dynamic pages, SSE, streaming audio/video, long reports.
  • Content-Length and chunked are mutually exclusive — the specification prohibits the simultaneous use of these headers.
  • Advantages — reduced latency, memory savings, ability to transmit infinite streams, and streaming processing on the client side.
  • Limitations — overhead from chunk headers, inability to show progress, issues with older proxy servers.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also