Series · Part 5 of 8
How HTTP/2 Works — The Speed Upgrade
HTTP/1.1 can only send one request at a time. A modern page needs 50+ files. HTTP/2 loads everything at once over a single connection — here's how multiplexing actually works.
HTTP works. You ask for something, the server responds.
But HTTP/1.1 has a fundamental constraint: one request at a time per connection.
A modern web page loads 50 to 100 separate files: HTML, CSS, JavaScript, fonts, images, API calls. With HTTP/1.1, these files have to wait in line. The browser asks for style.css, waits for the full response, then asks for app.js, waits, then asks for logo.png…
Browsers got clever. They’d open 6 parallel connections to the same server and download 6 files at once. It helped — but it meant 6 TCP handshakes, 6 TLS negotiations, 6 slow-start phases. The overhead compounded.
HTTP/2, released in 2015, eliminated the line entirely.
The core problem: head-of-line blocking
In HTTP/1.1, once you send a request, the connection is occupied until the full response arrives. If app.js is 500KB and takes 300ms to download, every other request behind it waits.
This is called head-of-line blocking — one large response blocks everything else in the queue.
Browsers worked around it by opening multiple connections (typically 6 per domain). But each connection is expensive: TCP handshake + TLS handshake + slow-start congestion window. You’re paying that cost 6 times, and you’re still limited to 6 parallel downloads.
Multiplexing: the HTTP/2 solution
HTTP/2 introduces streams — independent, numbered request-response pairs that share a single TCP connection.
Instead of:
Connection 1: [=== style.css ===] [=== app.js ===] [= logo.png =]
Connection 2: [== main.css ==] [======= video.mp4 =======]
Connection 3: [= favicon.ico =] [== data.json ==]
You get:
Single connection:
Stream 1: [= style.css ===]
Stream 2: [===== app.js ======]
Stream 3: [= logo.png =]
Stream 4: [== main.css ==]
Stream 5: [= favicon.ico =]
Stream 7: [== data.json ===]
All interleaved, all simultaneous
The browser opens one connection and multiplexes all its requests over it. There’s no queue. Each file downloads as fast as the server and network allow, independently of the others.
This eliminates the need for connection pooling hacks, and the single connection means you pay for TCP + TLS exactly once.
Binary framing
HTTP/1.1 is text-based. HTTP/2 uses a binary format.
Each HTTP/2 message is split into frames — small binary units. A single request might span multiple DATA frames. A response might be interleaved across the connection with other responses’ frames.
[HEADERS frame, stream 1] → request for style.css
[HEADERS frame, stream 3] → request for app.js
[DATA frame, stream 1] ← first bytes of style.css
[DATA frame, stream 3] ← first bytes of app.js
[DATA frame, stream 1] ← more of style.css
[DATA frame, stream 3] ← more of app.js (larger, arrives later)
Binary framing is why multiplexing is possible. It’s also why HTTP/2 connections can’t be inspected with telnet the way HTTP/1.1 could — it’s no longer human-readable text.
Header compression (HPACK)
HTTP/1.1 sends the full headers on every request. In a browser session, you send User-Agent, Accept, Cookie, and dozens of other headers — many of them identical — on every single request. For small API calls, the headers can be larger than the actual payload.
HTTP/2 compresses headers using HPACK:
-
Static table — 61 common headers are indexed by number. Instead of sending
content-type: application/json, you send the number31. Saves dozens of bytes. -
Dynamic table — Headers you send repeatedly get added to a shared table. After the first request, subsequent requests can reference them by index. A cookie sent 50 times is transmitted once and indexed after that.
On a typical news site, HPACK reduces header overhead by 85–95%.
Stream prioritization
The browser might want index.html and critical.css before it wants tracking-pixel.png. HTTP/2 lets the client assign weights and dependencies to streams.
Stream 1 (index.html): weight 256, no dependency
Stream 3 (critical.css): weight 220, depends on stream 1
Stream 5 (app.js): weight 148, depends on stream 3
Stream 7 (images): weight 36, no dependency
The server tries to honor these priorities, sending higher-priority frames first. In practice, browser prioritization heuristics have improved significantly and real-world impact varies — but render-blocking resources do benefit.
What doesn’t change
HTTP/2 is a transport-layer upgrade. Everything above it stays the same:
- Same URLs and routes
- Same HTTP methods (GET, POST, PUT, etc.)
- Same status codes (200, 404, 500, etc.)
- Same headers
- Same caching semantics
- Same cookies
Your application code doesn’t need to change. The browser and server negotiate HTTP/2 automatically during the TLS handshake (via ALPN extension) if both sides support it. You opt in at the server configuration level.
What HTTP/2 means for frontend development
Bundling is less important. HTTP/1.1 forced developers to concatenate all JS into one giant file to avoid making 50 sequential requests. With HTTP/2, 50 small modules can download simultaneously — often faster than one large bundle (which can’t render anything until it’s fully parsed).
Domain sharding makes things worse. The HTTP/1.1 trick of splitting assets across cdn1.example.com, cdn2.example.com, cdn3.example.com to get more parallel connections now adds overhead — extra DNS lookups, TCP handshakes, TLS negotiations, with no benefit.
Fewer spriting tricks needed. CSS sprites (combining all icons into one image) and data URIs were workarounds for HTTP/1.1 connection limits. HTTP/2 makes them unnecessary.
HTTP/3: the next step
HTTP/2 solved head-of-line blocking at the HTTP layer — but there’s still TCP-level head-of-line blocking. If one TCP packet is lost, all streams on that connection pause while TCP retransmits it.
HTTP/3 replaces TCP with QUIC — a custom protocol built on UDP that provides per-stream reliable delivery. A lost packet only blocks the one stream that contained it, not the entire connection.
HTTP/3 also combines the TCP and TLS handshakes into a single round trip (and even zero round trips for repeat connections). It’s designed for unreliable networks — mobile, satellite — where packet loss is frequent.
As of 2025, HTTP/3 carries over 30% of web traffic. All major browsers support it.
The takeaway
HTTP/2 is the default for modern web traffic. Every browser supports it. Every major web server supports it. Switching from HTTP/1.1 to HTTP/2 is typically a one-line server config change, and the performance improvement — especially on pages with many small resources — is real and measurable.
The key insight: multiplexing turns a single connection into a high-bandwidth channel where requests don’t wait for each other. Combined with header compression and stream prioritization, HTTP/2 makes the same page load noticeably faster without changing a line of application code.
Next up: HTTP is still ask-and-answer. What if the server needs to talk to you without you asking first? How WebSockets Work →
How the Internet Works · 8 of 8 published
- 0 What Happens When You Hit Enter?
- 1 How DNS Works — The Internet's Phone Book
- 2 How TCP Works — The Internet's Delivery Guarantee
- 3 How HTTPS Works — The Lock Icon Explained
- 4 How HTTP Works — The Language of the Web
- 5 How HTTP/2 Works — The Speed Upgrade
- 6 How WebSockets Work — Real-Time, Both Ways
- 7 How SFTP Works — Secure File Transfers
Related posts
SAP ERP Module Universe — Interactive Map
A visual guide to SAP's core ERP modules — Finance, Logistics, Manufacturing, HR, Analytics, and how they all connect through one database.
read more →
Why AI Forgets
Mid-conversation, AI suddenly doesn't remember what you said earlier. This isn't a bug — it's the context window. Here's how it works and how to work around it.
read more →Get new posts in your inbox
No spam. No digest. Just a note when I publish something new.
Discussion
On this page