1. Library
  2. Http and the Web
  3. Fundamentals

Updated 10 hours ago

Every time you click a link, light has to travel—sometimes across oceans. A packet from New York to Tokyo takes at minimum 70 milliseconds, and that's assuming perfect conditions. HTTP's entire history is humanity's refusal to accept that delay.

The protocol has been rewritten five times now. Not because engineers were bored, but because each version eventually hit a wall. Understanding those walls—and the clever hacks that got around them—explains why modern websites feel fast even when the physics hasn't changed.

HTTP/0.9: The One-Line Protocol

In 1991, Tim Berners-Lee needed a way to fetch documents. He built the simplest possible thing: connect to a server, send GET /index.html, receive HTML, disconnect. No headers. No error codes. No way to send images or know if something went wrong. Just text in, text out.

This was HTTP/0.9—sometimes called the "one-line protocol" because that's literally all it was. One line of request, one stream of response, connection closed.

The limitation was obvious: every single resource required a brand new connection. A page with ten images meant ten separate TCP handshakes—each one adding round trips of latency before any data could flow. But it worked. It proved the concept. The Web existed.

HTTP/1.0: Headers Change Everything

By 1996, people wanted to send more than just HTML. They wanted images, different document types, error messages that made sense. HTTP/1.0 added the machinery to make this possible: headers.

Headers are metadata. Content-Type: image/png tells the browser what it's receiving. User-Agent identifies who's asking. Status codes—200 OK, 404 Not Found, 500 Internal Server Error—gave servers a vocabulary to explain what happened.

HTTP/1.0 also added POST, so forms could submit data, and HEAD, so you could check if a resource existed without downloading it.

But the fundamental problem remained: one connection, one request. A page with thirty resources meant thirty TCP handshakes. The protocol itself had become the bottleneck.

HTTP/1.1: Keep the Line Open

HTTP/1.1, released in 1997, had one big idea: stop hanging up.

Persistent connections—also called keep-alive—let browsers reuse the same TCP connection for multiple requests. Request the HTML, get it, then immediately request the CSS over the same connection. No new handshake. The server just... waits for the next request.

This simple change cut latency dramatically. But it introduced a new problem: head-of-line blocking.

Picture a single-file line at a coffee shop. The person in front orders something complicated. Everyone behind them waits, even if they just want a black coffee. HTTP/1.1 worked the same way—requests processed in order, one at a time. A slow response blocked everything behind it.

Browsers worked around this by opening multiple connections in parallel—typically six per domain. But this was a hack, not a solution. Six lines at the coffee shop is better than one, but you're still waiting behind complicated orders.

HTTP/1.1 also made the Host header mandatory, which enabled virtual hosting—multiple websites sharing one IP address. Before this, every website needed its own IP. After this, a single server could host thousands of domains by checking which one you asked for.

For nearly two decades, HTTP/1.1 powered the Internet. It was good enough. Until it wasn't.

HTTP/2: Stop Waiting in Line

HTTP/2, standardized in 2015, attacked head-of-line blocking directly.

The insight was this: why process requests in order at all? If you're waiting for a large image, why should a tiny CSS file wait behind it? HTTP/2 introduced multiplexing—multiple requests and responses interleaved on a single connection, delivered as they're ready.

To make this work, HTTP/2 switched from text to binary framing. Messages get split into small frames, tagged with stream identifiers, and interleaved freely. The browser reassembles them on arrival. It's like having multiple conversations over one phone line, each sentence tagged with which conversation it belongs to.

Header compression via HPACK reduced another source of waste. HTTP headers are repetitive—the same cookies, the same user agent, the same host, request after request. HPACK maintains a table of previously sent headers and references them by index instead of retransmitting.

HTTP/2 also introduced server push: if the server knows you'll need style.css right after index.html, why wait for you to ask? Just send it. In practice, this proved harder to get right than expected—servers often guessed wrong—and adoption remained limited.

But HTTP/2 had a deeper problem it couldn't solve. The multiplexing happened at the HTTP layer, but underneath, everything still ran on TCP. And TCP has its own head-of-line blocking.

When a TCP packet gets lost, the protocol stalls the entire connection until that packet is retransmitted. Every stream, every resource, every frame—all waiting for one lost packet that might only affect one resource. HTTP/2 fixed the line at the restaurant, but everyone was still stuck in the same revolving door.

HTTP/3: Change the Door

HTTP/3, standardized in 2022, does something radical: it abandons TCP entirely.

The replacement is QUIC, a transport protocol built on UDP. QUIC implements reliability per-stream rather than per-connection. If a packet for one stream goes missing, only that stream stalls. Everything else keeps flowing.

This is true multiplexing with no head-of-line blocking at any layer. The problem HTTP/2 couldn't solve is simply... gone.

QUIC also combines the transport and encryption handshakes. Traditional HTTPS requires a TCP handshake, then a TLS handshake—multiple round trips before any application data can flow. QUIC does both at once, often establishing connections in a single round trip. For returning visitors, it can resume with zero round trips.

Because QUIC runs over UDP, it can evolve in application space. TCP is baked into operating system kernels—changing it requires coordinated updates across every device on the Internet. QUIC is just software. Updates deploy in weeks, not years.

QUIC also handles network changes gracefully. When your phone switches from WiFi to cellular, TCP connections break—the IP address changed, and TCP identifies connections by IP. QUIC uses connection IDs instead. The connection migrates seamlessly. You don't notice. Your video call doesn't drop.

The transition has been gradual but steady. Major browsers and content delivery networks support HTTP/3 by default now, falling back to HTTP/2 when needed. The improvements matter most where conditions are worst—lossy mobile networks, high-latency connections, exactly the situations where every millisecond of wasted waiting hurts.

The Pattern

Look at what each version actually did:

  • HTTP/0.9 (1991): One request, one connection. Simple but wasteful.
  • HTTP/1.0 (1996): Added headers and status codes. Still one request per connection.
  • HTTP/1.1 (1997): Reused connections. But requests still waited in line.
  • HTTP/2 (2015): Multiplexed requests. But TCP still blocked everything on packet loss.
  • HTTP/3 (2022): Replaced TCP with QUIC. No more blocking at any layer.

Each version maintained backward compatibility in meaning—the same methods, the same headers, the same semantics. What changed was the wire format, the transport, the mechanics of getting bits from here to there faster.

The speed of light hasn't changed. Physics still wins. But HTTP has gotten remarkably clever at not wasting the time physics gives us.

Frequently Asked Questions About HTTP Versions

Was this page helpful?

😔
🤨
😃