Konrad Kowalski (rootsher)Principal Platform & Reliability Architect110110111011110110110100011010110010100101100011

HTTP/2: Multiplexing Changed the Frontend Network Model

date
category
Networking
also in
Frontend
reading
2 min / 335 words

HTTP/2 keeps HTTP semantics but changes the wire format.

A request is no longer a textual block like:

http
GET /app.js HTTP/1.1

HTTP/2 uses binary frames.

Streams

Every request/response exchange belongs to its own stream.

A simplified model:

text
one TCP connection
│
├── stream 1: /app.js
├── stream 3: /styles.css
├── stream 5: /api/me
└── stream 7: /font.woff2

Frames from different streams can be interleaved.

text
S1 HEADERS
S3 HEADERS
S1 DATA
S5 HEADERS
S3 DATA
S1 DATA

That is multiplexing.

A slow response on one request does not stop the HTTP protocol from carrying frames of other streams.

Less need for many connections

HTTP/1.1 achieved concurrency through several TCP connections.

HTTP/2 can serve many parallel requests over a single connection.

For the frontend that reduces the point of techniques such as domain sharding.

One well used connection can be more efficient than several independent TLS/TCP connections.

Header compression

Many frontend requests carry similar headers:

text
Origin
Cookie
Accept
User-Agent
...

HTTP/2 uses HPACK to compress HTTP fields across messages on a connection.

That helps especially with many small requests.

It does not mean:

the number of requests stopped mattering.

Every request still has:

  • server work,
  • scheduling,
  • bytes,
  • application latency,
  • cache consequences.

HTTP/2 removes part of the transport cost, not the cost of a request existing.

TCP is still shared

Here comes the subtlety.

HTTP/2 multiplexes many streams, but all of them travel over one TCP byte stream.

If TCP loses a segment:

text
packet loss
  |
  v
TCP must recover missing bytes
  |
  v
later bytes wait

At the transport level that can temporarily hold back data belonging to many HTTP/2 streams.

So:

text
HTTP/2 stream independence
!=
transport independence

That is transport-level head-of-line blocking.

Prioritization is not a magic frontend scheduler

HTTP/2 has prioritization mechanisms, but the practical behavior depends on the browser, the server and the priority signaling in use.

The frontend should not design performance around the assumption:

text
browser will always schedule exactly as I imagine

A better tool remains control over the dependency graph of the page:

text
critical CSS
critical JS
lazy content
preload where justified

What do we see in DevTools?

In the waterfall many requests can start almost simultaneously.

That does not mean many TCP connections.

They may be separate HTTP/2 streams on one connection.

Which matters for performance analysis:

text
parallel requests
!=
parallel sockets

HTTP/2 solved multiplexing at the HTTP level.

But it still inherited TCP.

HTTP/3 keeps a similar streaming model of HTTP and moves it onto QUIC.