HTTP/2 flow control is fundamentally a credit-based system designed to prevent a fast sender from overwhelming a slow receiver, but most people think of it as just a way to limit bandwidth.
Let’s see it in action. Imagine a client requesting a large image.
# Client sends initial SETTINGS frame with WINDOW_UPDATE increment of 65535 (default)
# Server receives image data, sending WINDOW_UPDATE frames back to the client as it consumes data.
# If the server's buffer fills up, it stops sending WINDOW_UPDATE frames.
# The client, seeing its window is full, will stop sending DATA frames until it receives more credit.
The problem this solves is simple: in HTTP/1.1, a slow server could get swamped by a fast client, leading to dropped connections or degraded performance. HTTP/2’s flow control, managed per stream and per connection, ensures that neither side is forced to accept data it can’t handle. It operates using SETTINGS frames to establish initial window sizes and WINDOW_UPDATE frames to grant additional credit.
Here’s how it works internally:
- Initial Window Size: When a connection is established, both the client and server agree on an initial window size (default is 65,535 bytes) for each stream. This is the amount of data they are willing to receive before needing more credit.
- Data Transmission: The sender (e.g., the client sending a request body, or the server sending response data) can send up to its current window size for that stream.
- Window Update: As the receiver processes the data, it sends
WINDOW_UPDATEframes back to the sender, indicating how much space has become available in its receive buffer. This effectively grants more credit. - Stalling: If a receiver cannot process data fast enough, its receive buffer will fill up. It will stop sending
WINDOW_UPDATEframes. Consequently, the sender’s window for that stream will eventually reach zero, and it will stop sendingDATAframes for that stream, causing it to stall. - Connection-Level vs. Stream-Level: There are two levels of flow control:
- Stream-level: Each individual HTTP/2 stream has its own independent flow control window. This is crucial for multiplexing, allowing other streams to continue even if one is stalled.
- Connection-level: There’s also a flow control window for the entire connection.
WINDOW_UPDATEframes can be sent without a stream ID to affect the connection-level window.
The exact levers you control are primarily through the SETTINGS frame. These are typically sent at the beginning of a connection and can be updated.
SETTINGS_INITIAL_WINDOW_SIZE: This parameter in theSETTINGSframe controls the initial window size for all streams on a connection. A larger value means the sender can send more data before needing aWINDOW_UPDATE. For example, settingSETTINGS_INITIAL_WINDOW_SIZEto1048576(1MB) on the server might allow a client to burst more data initially.SETTINGS_MAX_FRAME_SIZE: While not directly flow control, this limits the maximum size of a singleDATAorHEADERframe. A smallerMAX_FRAME_SIZEcan lead to more frequent, smallerWINDOW_UPDATEframes, which might have a slight overhead but can allow for finer-grained credit management. The default is 16,384 bytes.
The interaction between stream-level and connection-level flow control is often misunderstood; a WINDOW_UPDATE frame with a stream ID effectively increments both the stream’s window and the connection’s window, minus the amount already accounted for by the connection-level update. If you send a WINDOW_UPDATE for stream 5 with a value of 10000, and the connection-level window has 50000 credit available, stream 5’s window will increase by 10000, and the connection’s available credit will decrease by 10000.
The one thing most people don’t realize is that the receiver always sends WINDOW_UPDATE frames to acknowledge data it has received, not necessarily data it has fully processed. This means a receiver can grant credit for data that is still sitting in its buffer, potentially leading to a stall if processing lags significantly behind reception.
The next problem you’ll likely encounter is understanding how to tune these SETTINGS parameters effectively for your specific network conditions and application workload.