Vasu Patel logoVasu Patel
← How the Bytes Travel: Networking
All chapters in this track
  1. 01Chapter 1: Why Networking Fundamentals Matter
  2. 02Chapter 2: The OSI Model and TCP/IP Stack, at a Working Level
  3. 03Chapter 3: Sockets, Ports, and the Connection Lifecycle
  4. 04Chapter 4: TCP vs UDP: Choosing the Right Transport
  5. 05Chapter 5: DNS Resolution, Step by Step
  6. 06Chapter 6: The HTTP Family: HTTP/1.1, HTTP/2, HTTP/3, and WebSockets
  7. 07Chapter 7: TLS/SSL and Certificates: The Handshake and Why HTTPS Matters
  8. 08Chapter 8: Load Balancers, Reverse Proxies, and CDNs
  9. 09Chapter 9: Latency, Bandwidth, and Congestion Control
  10. 10Chapter 10: API Protocol Choices: REST vs. gRPC vs. GraphQL, from a Networking Angle
  11. 11Chapter 11: Common Failure Modes: Timeouts, Connection Resets, Retry Storms, Thundering Herd
  12. 12Chapter 12: Where Networking Abstractions Leak: NAT, Middleboxes, and Other Surprises
  13. 13Chapter 13: Full Walkthrough: What Happens When You Type a URL and Press Enter
  14. 14Chapter 14: Common Networking Interview Questions
  15. 15Chapter 15: Networking Concepts in System Design and Production Debugging

Chapter 6 of 15

Chapter 6: The HTTP Family: HTTP/1.1, HTTP/2, HTTP/3, and WebSockets

Why this matters

HTTP is the protocol every web engineer uses daily, and yet the specific differences between HTTP/1.1, HTTP/2, and HTTP/3 — and why each new version exists — are frequently hazy even for experienced engineers. Each version is a direct, traceable response to a specific limitation of the one before it; understanding that chain of cause and effect makes the differences memorable and meaningful rather than a list of version numbers to memorize.

HTTP/1.1: the baseline, and its core limitation

[BEGINNER] HTTP/1.1 is a simple, text-based request/response protocol running over TCP. Its most consequential design decision, given Chapter 4's TCP head-of-line blocking discussion: on a single TCP connection, HTTP/1.1 requests must be sent and fully responded to essentially one at a time (or with limited, awkward pipelining that saw little real-world adoption) — a genuine head-of-line blocking problem, at the application protocol level, layered directly on top of TCP's own.

$ curl -v http://example.com/
> GET / HTTP/1.1
> Host: example.com
> Connection: keep-alive       <-- reuses the SAME TCP connection for subsequent requests
>
< HTTP/1.1 200 OK
< Content-Type: text/html

The workaround browsers used for years: open multiple parallel TCP connections to the same host (typically 6 per host) specifically to work around this one-request-at-a-time limitation — which works, but at the cost of multiple separate TCP handshakes (and TLS handshakes, Chapter 7) instead of reusing one connection efficiently.

HTTP/2: multiplexing over one connection

[BEGINNER] HTTP/2 solves HTTP/1.1's application-level head-of-line blocking by introducing multiplexing: many requests and responses can be in flight simultaneously, interleaved as small "frames" over a single TCP connection, each frame tagged with a stream ID so the receiver can reassemble each request/response independently.

HTTP/1.1: [====Request 1====][====Response 1====][====Request 2====][====Response 2====]
          (strictly one at a time on this connection)

HTTP/2:   [Req1-frame][Req2-frame][Resp1-frame][Req3-frame][Resp2-frame][Resp3-frame]...
          (interleaved on ONE connection — no need to wait for one exchange to fully finish)

This eliminates the application-level head-of-line blocking that drove browsers to open six parallel connections — one well-managed HTTP/2 connection now handles what used to require several HTTP/1.1 connections, with real benefits: fewer total handshakes, better use of a single TCP connection's congestion window (Chapter 9), and simpler connection management overall. HTTP/2 also adds header compression (HPACK) — since HTTP/1.1 sends full, repetitive headers on every single request, compressing them meaningfully reduces overhead for sites making many requests to the same host.

[SENIOR INSIGHT] HTTP/2 does not eliminate head-of-line blocking — it moves it down a layer. Multiplexing happens at the HTTP layer, but it all still rides on one TCP connection underneath. If that single TCP connection experiences packet loss, TCP's own reliable-in-order delivery (Chapter 4) blocks all the multiplexed HTTP/2 streams behind the lost packet — even streams that have nothing to do with the lost data — because TCP doesn't know or care about HTTP/2's stream boundaries; it just sees one ordered byte stream. This is precisely the motivation for HTTP/3.

HTTP/3: multiplexing without TCP's head-of-line blocking

[BEGINNER] HTTP/3 runs over QUIC (introduced conceptually in Chapter 4) instead of TCP. Because QUIC implements its own per-stream reliability directly (rather than relying on one single TCP ordered byte stream for everything), a lost packet affecting one HTTP/3 stream does not block delivery of other, independent streams on the same connection — QUIC's loss recovery is scoped to the specific stream that lost data, not the entire connection.

HTTP/2 over TCP:  one lost packet -> ALL multiplexed streams stall until it's retransmitted
HTTP/3 over QUIC: one lost packet -> only the AFFECTED stream stalls; others continue delivering

QUIC additionally folds the TLS handshake (Chapter 7) into its own connection-establishment handshake, reducing the number of round-trips needed before the first request can be sent compared to TCP+TLS's traditionally separate handshakes — a further latency win, especially significant on high-latency connections like mobile networks.

WebSockets: a genuinely different model

[BEGINNER] Everything above is still fundamentally request/response — the client asks, the server answers. WebSockets provide something structurally different: after an initial HTTP handshake (an Upgrade request that converts the connection from HTTP to the WebSocket protocol), the connection becomes a full-duplex, persistent channel where either side can send messages to the other at any time, with no need for the client to "ask" first.

Client                                          Server
  |--- HTTP GET /chat, Upgrade: websocket ------>|
  |<-- HTTP 101 Switching Protocols -------------|
  |                                                |
  |========= now a persistent, bidirectional =====|
  |========= WebSocket connection, NOT HTTP =======|
  |                                                |
  |--- message: "hello" ------------------------->|
  |<-- message: "new chat message from someone" --|   (server-initiated, unprompted by any request)
  |<-- message: "another update" -----------------|

When to use WebSockets versus regular HTTP requests (even HTTP/2's efficient multiplexing): WebSockets fit genuinely bidirectional, server-initiated, low-latency scenarios — chat applications, live collaborative editing, real-time notifications, live price feeds — where the server needs to push data to the client without waiting for the client to ask. Regular request/response HTTP (even HTTP/2/3) remains the right fit for the vast majority of web traffic, where the client-initiated request/response model actually matches the interaction pattern.

Summary comparison

VersionTransportKey innovationRemaining limitation
HTTP/1.1TCPSimple text protocol, keep-alive connectionsOne request in flight per connection (application-level head-of-line blocking)
HTTP/2TCPMultiplexing (many streams, one connection), header compressionTCP-level head-of-line blocking still affects all streams on packet loss
HTTP/3QUIC (over UDP)Per-stream reliability — loss on one stream doesn't block others; faster handshakeNewer, so has had less time for universal middlebox/network compatibility (Chapter 12)
WebSocketsTCP (starts as HTTP, then upgrades)True bidirectional, persistent, server-initiated messagingNot a fit for simple request/response interactions — different model entirely

Common mistakes

  1. Assuming HTTP/2 solved head-of-line blocking completely, missing that it only solved the application-layer version, while TCP-level head-of-line blocking still affects all multiplexed streams on packet loss — this is precisely why HTTP/3 exists at all.
  2. Reaching for WebSockets for interactions that are actually simple request/response, adding real complexity (managing a persistent connection, reconnection logic, message framing) where a normal HTTP request would have been simpler and sufficient.
  3. Not recognizing that browsers historically opened six parallel connections per host specifically as an HTTP/1.1 workaround — a detail that explains otherwise-mysterious historical browser networking behavior and configuration limits.
  4. Treating "newer protocol version" as automatically better in every situation without considering Chapter 12's point about newer protocols (HTTP/3/QUIC specifically) sometimes having rougher compatibility with older network middleboxes, firewalls, and infrastructure that expect traditional TCP traffic.

Discussion / interview questions

  1. "What problem does HTTP/2's multiplexing solve, and how does it work?" Strong answer: Solves HTTP/1.1's one-request-at-a-time-per-connection limitation by interleaving multiple requests/responses as tagged frames over a single TCP connection, letting many exchanges progress concurrently without needing multiple separate connections.

  2. "Why does HTTP/3 exist if HTTP/2 already solved head-of-line blocking with multiplexing?" Strong answer: HTTP/2's multiplexing still rides on one TCP connection, so TCP's own strict in-order delivery means a single lost packet blocks ALL multiplexed streams, not just the affected one; HTTP/3 runs over QUIC instead, which implements independent per-stream reliability so loss on one stream doesn't block the others.

  3. "How do WebSockets differ fundamentally from HTTP request/response, even HTTP/2's efficient multiplexed version?" Strong answer: WebSockets establish a persistent, full-duplex channel where either side can send messages at any time with no requesting side needed; HTTP (any version) remains fundamentally a client-asks, server-answers model, even when multiplexed.

  4. "When would you choose WebSockets over polling or even HTTP/2 server-initiated techniques for a real-time feature?" Strong answer: When the server needs to push data to the client at arbitrary times with low latency and the interaction is genuinely bidirectional (chat, live collaboration, real-time feeds) — not for interactions that are still fundamentally request/response, where the added complexity of managing a persistent connection isn't justified.

Chapter summary / cheat sheet

  • HTTP/1.1: simple, text-based, one request in flight per TCP connection — application-level head-of-line blocking, historically worked around with multiple parallel connections.
  • HTTP/2: multiplexes many streams over one TCP connection (fixing application-level head-of-line blocking) plus header compression — but TCP-level head-of-line blocking still affects all streams together on packet loss.
  • HTTP/3: runs over QUIC (UDP-based) with independent per-stream reliability, eliminating the TCP-level head-of-line blocking that HTTP/2 still had, plus a faster combined connection/TLS handshake.
  • WebSockets: a structurally different, persistent, full-duplex model — not a version of HTTP's request/response evolution, but a distinct protocol (reached via an HTTP upgrade) for genuinely bidirectional, server-initiated communication.
  • Each version's existence is a direct, traceable fix for a specific limitation in the version before it — understanding that chain is more durable than memorizing feature lists per version.