Hotshard
Open the simulatorSimulator
Networking at scale

HTTP/2 & HTTP/3

One connection, many requests at once — and why a lost packet still matters.

HTTP gave you the request/response language, TCP gave you a reliable byte pipe, and TLS made that pipe private. But there's a bottleneck none of them fixed: over classic HTTP/1.1, a single connection can only work on one request at a time, so one slow response stalls everything queued behind it. A modern web page pulls dozens of resources, and that queueing is felt as jank. HTTP/2 fixes it by letting many requests share one connection at once — multiplexing. HTTP/3 goes further, swapping out TCP itself to fix a subtler stall that HTTP/2 left behind. This is the story of one problem — head-of-line blocking — chased down through three versions of the protocol, and it's why the web feels faster than it did a decade ago.

Open the simulator →~15 min read

Start here: one slow response stalls the whole page#

TL;DRthe 30-second version
  • Over HTTP/1.1 a connection handles one request/response at a time. A slow or large response blocks every request queued behind it on that connection — head-of-line blocking. Browsers hack around it by opening ~6 parallel connections per host, which is wasteful and still caps out.
  • HTTP/2 puts many requests on one connection at once (multiplexing): each request is a stream, messages are chopped into small frames, and frames from different streams are interleaved on the wire. A slow response no longer blocks the others — its frames just take their turn.
  • But HTTP/2 still runs on TCP, which delivers all bytes strictly in order. One lost packet makes TCP hold back every byte after it until the retransmission arrives — so all streams stall together. That's head-of-line blocking again, moved down to the transport layer.
  • HTTP/3 replaces TCP with QUIC (a new transport over UDP) that gives each stream its own independent delivery. Now a lost packet only stalls its own stream; the rest keep flowing. QUIC also folds in the TLS 1.3 handshake, so connections set up in fewer round trips.

Open a single web page and your browser doesn't make one request — it makes dozens: the HTML, then the CSS, the JavaScript, fonts, and a stream of images. In HTTP/1.1, a connection is strictly one-at-a-time: the client sends a request, waits for the whole response, and only then can send the next request on that connection. The responses come back in the order the requests went out, one after another.

That ordering is the problem. Suppose the first thing you request is a large hero image and the next two are tiny CSS files. On one connection, the two small files can't come back until the big image finishes — they're stuck in line behind it, even though they'd take milliseconds on their own. This is head-of-line blocking: the item at the front of the line holds up everything behind it. The response that happens to be first, whatever its size, gates the rest.

The workaround that reveals the problemBrowsers cope with HTTP/1.1's one-at-a-time limit by opening several connections to the same server in parallel — typically up to six. That's six requests in flight instead of one, but no more: a page with sixty resources still queues them six-at-a-time, and every extra connection costs its own TCP and TLS handshake plus a fresh start for congestion control. (Congestion control is TCP's mechanism for probing how fast it can send without swamping the network: it begins cautiously and ramps up, so a brand-new connection is slow before it gets up to speed.) Sites even split assets across multiple hostnames ('domain sharding') just to unlock more connections. When the fix is 'open more connections and lie about your hostnames,' the protocol itself is the thing to fix.

HTTP/2's fix: many requests on one connection#

HTTP/2 keeps everything you know about HTTP — the same methods, paths, headers, and status codes — but changes how the messages are packaged and sent. The headline feature is multiplexing: carrying many independent request/response exchanges over a single connection at the same time. To do it, HTTP/2 introduces two ideas.

  • A stream is one request/response exchange — one logical conversation — living inside the connection. A single HTTP/2 connection can hold many streams open at once, each with its own id. Think of the connection as a highway and each stream as a car travelling on it independently.
  • A frame is a small chunk of one stream's data — a piece of a request or response, tagged with which stream it belongs to. Instead of sending a whole response as one uninterrupted block (the HTTP/1.1 way), HTTP/2 slices every message into frames.

Because every frame carries its stream id, the sender can interleave frames from different streams on the wire — a few frames of the big image, then a few frames of a CSS file, then back to the image — and the receiver uses the ids to reassemble each stream correctly. That interleaving is multiplexing in action: the big image and the small files now make progress together on one connection, so a slow response no longer blocks the others. The head-of-line blocking from the last section is gone — at least, the version of it that lived in HTTP itself.

ClientbrowserServerone connection
Three requests opened as streams 1 (image), 3 (css), 5 (js).
frame [stream 1] image…
frame [stream 3] css (done)
frame [stream 5] js (done)
frame [stream 1] image…
Small files finish early — not stuck behind the image
One connection, frames from three streams interleaved

Two supporting changes make this practical. First, HTTP/2 is a binary protocol: frames are compact binary structures, not the plain text of HTTP/1.1 — easier and cheaper for machines to parse and frame precisely. Second, headers are compressed with a scheme called HPACK, which avoids re-sending the same headers (cookies, user-agent) in full on every request; since many requests to a site carry nearly identical headers, that saves real bytes. Neither changes the meaning of HTTP — they just make the multiplexed connection efficient.

The catch: HTTP/2 still rides on TCP#

HTTP/2 solved head-of-line blocking inside HTTP — but it runs on top of TCP, and TCP has a head-of-line problem of its own that HTTP/2 can't escape. To see it, recall one guarantee TCP makes: it delivers the bytes of its stream in exactly the order they were sent, with no gaps. Your application never sees byte 1,001 until it has seen byte 1,000.

That guarantee is wonderful for a single file and painful for a multiplexed connection. On the wire, all those interleaved frames from all those streams are travelling as one ordered TCP byte stream, carried in packets (a packet is the unit TCP actually sends over the network). If one packet goes missing — normal on the internet — TCP must wait for it to be retransmitted before it will hand any later bytes to the application, because it refuses to deliver out of order. So even though the lost packet only carried frames for one stream, every other stream's frames that arrived safely are held hostage behind the gap. All streams stall until the retransmission lands.

Head-of-line blocking, one layer downThis is the same phenomenon as the HTTP/1.1 problem, but relocated. HTTP/2 removed the blocking at the application layer (no request waits behind another request), yet a single dropped packet still blocks every stream at the transport layer — because TCP's in-order guarantee treats the whole multiplexed connection as one indivisible stream of bytes. On a clean network you never notice; on a lossy mobile link, HTTP/2 can actually behave worse than several HTTP/1.1 connections, because with six connections a lost packet only stalls one of them.
PredictHTTP/2 puts all your streams on one TCP connection to avoid the cost of many connections. On a network with 2% packet loss, why might that single connection sometimes deliver a page more slowly than six separate HTTP/1.1 connections would?

Hint: A lost packet stalls one TCP connection. How many streams share that connection in each case?

Because TCP's in-order delivery makes one lost packet stall everything sharing that connection. With HTTP/2's single connection, a dropped packet holds back frames for all streams until it's retransmitted — the whole page pauses. With six independent HTTP/1.1 connections, a lost packet only blocks the one connection it happened on; the other five keep delivering. So concentrating all streams onto one TCP connection concentrates the blast radius of every packet loss. This is exactly the transport-layer head-of-line blocking that HTTP/3 was designed to remove — and why the fix had to happen below HTTP, in the transport itself.

HTTP/3's fix: replace TCP with QUIC

You can't fix transport-layer head-of-line blocking by changing HTTP, because the blocking lives in TCP. So HTTP/3 does the radical thing: it stops using TCP. It runs on QUIC — a new transport protocol built on top of UDP (the internet's bare, unordered datagram service) that re-implements the good parts of TCP (reliability, congestion control, ordering) but with one crucial difference: QUIC understands streams natively.

In QUIC, each stream is delivered independently. The trick is that QUIC tracks the bytes of every stream separately — much as HTTP/2 tags each frame with its stream id — so it always knows which stream a lost packet's data belonged to. It still guarantees in-order, reliable delivery within a single stream, but makes no ordering promise across streams. So when a packet is lost, QUIC only needs to hold back the one stream whose bytes were in that packet — every other stream keeps being delivered to the application without waiting. The lost image packet stalls the image; the CSS and JS, whose data arrived fine, are handed over immediately. The transport-layer head-of-line blocking is gone, because the transport no longer treats the connection as one indivisible byte stream.

Why QUIC also connects fasterBecause QUIC is new, it was designed with encryption built in: the TLS 1.3 handshake is folded directly into QUIC's connection setup instead of running as a separate layer on top. Where HTTP/2 pays a TCP handshake and then a TLS handshake (two sets of round trips), HTTP/3 does both together — often a single round trip. A resumed connection can even reach zero: it reuses keys cached from a previous visit, so it sends encrypted data in its very first packet with no setup round trip at all. Faster setup is a bonus on top of the head-of-line fix. (See the TLS topic for the 1-RTT/0-RTT handshake this reuses.)

QUIC also fixes a smaller annoyance: it labels each connection with a connection id it assigns, rather than identifying it by the source and destination IP addresses and ports the way TCP does. So when your phone switches from Wi-Fi to cellular and its IP address changes, a TCP connection would break — but the QUIC connection, still recognized by its id, keeps going. That's connection migration: the same download continues as your network changes underneath it.

What each version costs to set up#

The versions differ most in two places: how many round trips it takes before the first byte of data can flow, and how a single connection behaves under packet loss. A round trip (RTT) is one out-and-back across the network; on a link 60 ms each way it's real, human-visible time, and setup handshakes are counted in round trips.

HTTP/1.1HTTP/2HTTP/3
Requests per connectionOne at a timeMany (multiplexed)Many (multiplexed)
TransportTCPTCPQUIC (over UDP)
App-layer head-of-lineYes — one response blocks the nextSolvedSolved
Transport head-of-linen/a (one stream)Yes — a lost packet stalls all streamsSolved — per-stream delivery
Setup round trips (with TLS)TCP + TLS (≈2–3 RTT)TCP + TLS (≈2–3 RTT)QUIC = TLS folded in (≈1 RTT, 0 on resume)

The practical upshot: HTTP/2 is a large win over HTTP/1.1 on almost any network, because eliminating per-request queueing and six-connection overhead helps every page. HTTP/3's extra win shows up most on lossy, high-latency links (mobile, congested Wi-Fi), where transport-layer head-of-line blocking and slow multi-handshake setup hurt the most. On a fast, clean wired connection the gap between /2 and /3 is smaller — the loss it fixes rarely happens.

What you give up#

VersionBuys youCosts you
HTTP/2Multiplexing, header compression, one connectionStill TCP head-of-line; stream prioritization is complex; server push (the server sending resources you never asked for) was a flop and is being removed
HTTP/3No transport head-of-line, faster setup, connection migrationUDP is sometimes throttled or blocked by networks; more CPU (encryption in userspace); newer, less battle-tested tooling

The deeper trade in HTTP/3 is where the work moves. TCP lives in the operating system kernel and is decades-hardened; QUIC runs largely in user space (inside the browser or server process), which is what let it be deployed and iterated quickly without waiting for every OS to update — but it also means more CPU per byte and reinventing reliability logic that TCP had long since gotten right. The bet — that flexibility and the head-of-line fix are worth the CPU and the risk — has largely paid off, but it is a real bet, not a free lunch.

The through-line: chasing one problem down the stack#

Read the three versions as one story about head-of-line blocking, fixed at a lower layer each time. HTTP/1.1 has it at the application layer: one request waits behind another. HTTP/2 removes that with multiplexing but inherits it from TCP at the transport layer: one packet loss stalls every stream. HTTP/3 removes that too by replacing TCP with QUIC, whose per-stream delivery means a loss only affects its own stream. Each version pushed the fix one layer deeper because that's where the remaining blocking lived.

Layer where blocking livesHTTP/1.1HTTP/2HTTP/3
Application (request behind request)PresentFixed by multiplexingFixed
Transport (packet loss stalls streams)Present (TCP)Fixed by QUIC
In the wild
  • HTTP/2 is everywhere: most sites and CDNs (Cloudflare, Akamai, Fastly) have served it for years, and browsers only enable it over HTTPS, so in practice HTTP/2 implies TLS.
  • gRPC is built on HTTP/2 specifically for its multiplexing — many concurrent remote-procedure-call (RPC) requests and long-lived streaming calls share one connection, which is exactly what HTTP/2 streams provide.
  • QUIC and HTTP/3 came out of Google (deployed across Chrome, YouTube, and Google Search before standardization) and are now an IETF standard, served by Cloudflare, Google, and Meta; browsers discover HTTP/3 support via an Alt-Svc header and upgrade.
  • HTTP/2 Server Push — the server proactively sending resources it thinks you'll need — sounded great but rarely helped in practice (it often pushed things the browser already cached) and has been removed from Chrome; the ecosystem moved to the simpler `103 Early Hints` instead, where the server merely sends an early response hinting at resources to fetch and the browser decides whether to request them.
  • Connection migration keeps a QUIC download alive across a Wi-Fi-to-cellular switch, one reason video and large-file services favor HTTP/3 on mobile.
Pitfalls & gotchas
Isn't multiplexing just HTTP/1.1 pipelining, which already existed?

No — and the difference is the whole point. HTTP/1.1 pipelining let a client send several requests without waiting, but the server still had to return the responses in order, so a slow first response blocked the rest (head-of-line blocking remained) — which is why pipelining was effectively never usable and stayed disabled. HTTP/2 interleaves frames from different streams, so responses can complete in any order. That's true concurrency on one connection, not just early sending.

Does HTTP/2 require HTTPS?

The spec technically allows plaintext HTTP/2, but no major browser implements it — they only negotiate HTTP/2 over a TLS connection. So in practice, using HTTP/2 (or HTTP/3) means using HTTPS. The version is negotiated during the TLS handshake via a field called ALPN.

If HTTP/2 solved head-of-line blocking, why does the problem keep coming up?

Because 'head-of-line blocking' happens at whatever layer forces in-order handling. HTTP/2 fixed it at the application layer (requests no longer queue behind each other) but not at the transport layer (TCP still delivers all bytes in order, so a lost packet stalls every stream). They're the same idea at two different layers; HTTP/3 was needed to fix the transport one.

Is HTTP/3 always faster than HTTP/2?

No. Its biggest wins — no transport head-of-line blocking and faster setup — show up on lossy or high-latency networks. On a fast, clean connection with little packet loss, HTTP/2 and HTTP/3 perform similarly, and HTTP/3's extra CPU cost can even make it marginally slower. It's a clear win on mobile and a wash-to-small-win on good wired links.

Why UDP? Isn't UDP unreliable?

UDP itself is unreliable (no ordering, no retransmission) — but QUIC builds reliability, ordering, and congestion control on top of UDP, per stream. QUIC uses UDP only as a minimal delivery layer that networks already pass through, precisely because it couldn't add new features to TCP (which is baked into every OS kernel and every middlebox — the firewalls, routers, and NATs sitting between you and the server). UDP was the blank canvas; QUIC paints reliability back on.

QuizA page loads a large image and two small scripts over a single HTTP/2 connection. Midway, one packet carrying image data is lost. What happens to the two scripts, whose data all arrived safely?

  1. They're delivered immediately — HTTP/2 multiplexing keeps streams independent.
  2. They stall until the lost image packet is retransmitted, because TCP delivers all bytes in order.
  3. They're re-requested on a new connection automatically.
  4. Only the image stalls; the scripts continue.
Show answer

They stall until the lost image packet is retransmitted, because TCP delivers all bytes in order.This is transport-layer head-of-line blocking. HTTP/2's streams are independent at the application layer, but they all ride one TCP connection, and TCP won't hand any bytes to the application past a gap until the missing packet is retransmitted. So the safely-arrived script bytes are held hostage behind the lost image packet — all streams stall. Option 4 describes HTTP/3 over QUIC, which gives each stream independent delivery; that's exactly the fix HTTP/3 adds.

In an interview

This topic rewards a candidate who can tell the one-problem-three-versions story instead of reciting feature lists. Lead with head-of-line blocking and trace it down the layers — that framing shows you understand why each version exists, not just what it added.

  • Define multiplexing precisely: many streams on one connection, messages split into frames tagged by stream id and interleaved — not 'HTTP/2 is faster.'
  • Name the two layers of head-of-line blocking: application (HTTP/1.1 → fixed by HTTP/2) and transport (TCP → fixed by HTTP/3's QUIC). Conflating them is the common miss.
  • Know why HTTP/3 needed a new transport: the remaining blocking was in TCP's in-order delivery, which HTTP can't change — so QUIC re-did the transport over UDP with per-stream delivery.
  • Mention the practical nuance: HTTP/3's win is largest on lossy/high-latency (mobile) links; on clean wired networks /2 and /3 are close.
  • Connect to what you know: gRPC rides HTTP/2 for multiplexing; QUIC folds in the TLS 1.3 handshake for faster setup; browsers negotiate the version via ALPN during the TLS handshake.
PredictAn interviewer asks: 'We serve a high-traffic API to mobile clients on flaky networks. Would you move from HTTP/2 to HTTP/3, and what's the risk?' What's the strong answer?

Hint: What does flaky mobile actually stress — and what does HTTP/3 specifically fix about it? Then: what breaks if you drop HTTP/2 entirely?

Yes, lean toward HTTP/3, because the failure mode you'll hit most on flaky mobile networks is exactly what HTTP/3 fixes: TCP's transport-layer head-of-line blocking, where one lost packet stalls every multiplexed stream, plus slow multi-round-trip setup on high latency. QUIC's per-stream delivery and folded-in TLS handshake target both. The honest risks: some restrictive networks throttle or block UDP (so you keep HTTP/2 as a fallback — which browsers do automatically via Alt-Svc), it costs more server CPU (encryption in user space), and the tooling/observability is younger. So the answer is 'yes, with HTTP/2 fallback and an eye on CPU,' not an unconditional yes — naming the fallback and the cost is what makes it strong.

References
References

Feedback on this topic →