WierdX — Programming Reference All tutorials →
Developer reference · Practical tutorials · CS fundamentals
HTTP & Networking

HTTP/2 and HTTP/3: What Changed and Why It Matters

HTTP/1.1 shipped in 1997 and was designed around one request per connection. Every optimization since — sprite sheets, domain sharding, inlined CSS — was a workaround for that limitation. HTTP/2 and HTTP/3 remove the limitation instead.

Published July 6, 2026

Under HTTP/1.1, a connection handles one request at a time. Ask for a second resource before the first response finishes and the browser either queues it behind the first on the same connection or opens a new TCP connection entirely, which is why browsers historically capped concurrent connections per hostname at around six and why sites split assets across multiple subdomains just to get more parallel connections. Every one of those workarounds exists because the protocol itself couldn't multiplex.

HTTP/2: one connection, many streams

HTTP/2 solves this at the protocol level. A single TCP connection carries multiple independent streams, each representing one request/response pair, interleaved as binary frames rather than sent as sequential plain text. The browser can ask for the HTML, the CSS, and a dozen images all on the same connection, and the server can start sending responses for all of them without waiting for earlier ones to finish. This is why the constellation of HTTP/1.1-era hacks stopped making sense: concatenating files into one bundle to save round trips, sharding assets across subdomains to dodge the six-connection cap, inlining small images as data URIs. Under HTTP/2 those techniques often make things worse, because a single large bundle can't be cached or invalidated granularly, and splitting across domains just means paying for multiple TLS handshakes instead of reusing one multiplexed connection.

HTTP/2 also compresses headers with HPACK, which matters more than it sounds like it should — a typical request carries several hundred bytes of repetitive header data (cookies, user-agent, accept-encoding) on every single request, and HPACK maintains a shared compression context between client and server so repeated header values get sent as small references instead of full strings after the first request. And it added server push, letting a server proactively send a resource it knows the client will need (the CSS file referenced by the HTML it just requested) without waiting for the client to ask — though push turned out to be hard to use correctly and browsers have been dropping support for it, since guessing wrong about what the client needs wastes bandwidth more often than it saves round trips.

The problem HTTP/2 didn't fix

HTTP/2 multiplexes streams at the application layer, but they all still ride on one TCP connection, and TCP guarantees in-order delivery of bytes. If a single packet gets lost, TCP has to hold every byte behind it — including bytes belonging to streams that have nothing to do with the lost packet — until a retransmission arrives. That's head-of-line blocking moved down a layer: HTTP/2 solved it at the request level and reintroduced it at the transport level. On a lossy connection (mobile networks, congested Wi-Fi), one dropped packet can stall every in-flight request on the connection, even ones whose data already fully arrived.

HTTP/3: replacing TCP with QUIC

HTTP/3 fixes this by dropping TCP entirely and running over QUIC, a transport protocol built on UDP. QUIC implements its own reliability and ordering, but critically, it does so per-stream rather than per-connection: a lost packet only blocks the stream it belonged to, and every other stream on the same QUIC connection keeps flowing. That directly removes the transport-level head-of-line blocking HTTP/2 couldn't avoid.

QUIC also folds the TLS handshake into the connection setup instead of running it as a separate step after the transport handshake, which cuts a round trip off every new connection, and on a reconnect to a server you've talked to recently it can support sending data on the very first packet (0-RTT). It handles connection migration too — a QUIC connection is identified by a connection ID rather than the traditional IP-and-port tuple, so a phone switching from Wi-Fi to cellular can keep the same connection alive instead of every in-flight request failing and restarting, which TCP has no way to do.

What this means in practice

Almost none of this requires application code changes. Browsers negotiate the protocol version automatically via ALPN during the TLS handshake, and most CDNs and modern web servers (Nginx, Caddy, cloud load balancers) support HTTP/2 and increasingly HTTP/3 as a configuration flag, not a rewrite. The practical takeaway for someone building a web app is mostly about un-learning HTTP/1.1-era performance advice: stop domain sharding, stop bundling everything into one giant JS file purely to save requests, and let the protocol multiplex naturally. Where it still matters to think about actively is on connections you don't control the quality of — mobile clients on unreliable networks benefit measurably from HTTP/3's per-stream loss recovery in ways a desktop browser on stable broadband mostly won't notice. The formal specifications, RFC 9113 for HTTP/2 and RFC 9114 for HTTP/3, are dense but readable if you want the wire-format details rather than a summary.