Skip to main content

A History of the HTTP Protocol

· 11 min read

HTTP is the primary protocol for communication between browsers and servers. It's an application-layer protocol (layer 7): data produced at the application layer rides on a transport-layer protocol to reach other hosts on the internet, and that carrier is TCP (replaced by UDP in 3.0) — establish a connection over TCP, then transfer the content.

In the 1960s, the U.S. Department of Defense's Advanced Research Projects Agency (ARPA) built ARPANET, widely regarded as the origin of the internet. In the 1970s, drawing on their experience with ARPANET, researchers invented the famous TCP/IP protocol suite. With its clean layered design and stable performance, it made its way into the UNIX kernel in the mid-1980s, bringing many more computers onto the network.

In 1989, Dr. Tim Berners-Lee published a paper proposing a system of hyperlinked documents built on top of the internet. In it he established three key technologies: URI, HTML, and HTTP.

With these three technologies, a hypertext system could run beautifully on the internet, and Berners-Lee named it the "World Wide Web". HTTP began to spread across the internet — today the most common versions are HTTP/1.1 and HTTP/2.0.

Every generation of HTTP has been an optimization built on the shortcomings of the one before it.

HTTP/0.9

HTTP (HyperText Transfer Protocol) was officially born in 1991, at version 0.9. As the name suggests, its job was to transfer hypertext content — HTML. The protocol defined a communication model where the client sends a request and the server responds. The request message was a single line: GET plus the path of the requested file. With no way to distinguish message types, it could only transfer text.

HTTP/1.0

As the internet grew and browsers appeared, plain text was no longer enough. Browsers wanted HTTP to carry scripts, stylesheets, images, audio, video — files of all kinds. So the 1.0 version, released in 1996, introduced the following:

  • New methods such as HEAD and POST
  • Response status codes to indicate possible error causes
  • The concept of a protocol version number
  • The concept of HTTP headers, making request and response handling much more flexible
  • Transferred data no longer limited to text

The most fundamental change was the header mechanism, with header content expressed as key-value pairs. The request headers use the Accept field to tell the server which file types the client can receive, and the response headers use Content-Type to tell the browser what kind of file is being returned. Header fields solved more than content-type negotiation — they enabled caching, authentication, and many other features.

HTTP/1.1

With the internet's rapid growth, HTTP/1.0 soon fell short too, and the core issue was connections. Every HTTP/1.0 exchange had to go through three phases: establish the connection, transfer the data, and tear down the connection. When a page referenced many external files, all that connecting and disconnecting added substantial network overhead.

HTTP/1.1 made Connection: keep-alive the default, avoiding the cost of repeatedly setting up and tearing down connections. But the server had to return results strictly in the order the client sent its requests, so the client could match each response to its request — using header fields to determine whether the current response had been fully received, with no two responses allowed in flight at once.

To address HTTP/1.0's problems, HTTP/1.1, released in 1999, brought the following:

  • Persistent connections (Connection: keep-alive): TCP connection reuse — a TCP connection stays open by default and can be reused across multiple requests
  • Concurrent connections: multiple persistent connections allowed per domain (easing the head-of-line blocking inherent to persistent connections)
  • Pipelining, letting one TCP connection send multiple requests at once (responses must come back in request order, so it saw little use)
  • New methods: PUT, DELETE, OPTIONS, PATCH
  • New caching fields (Cache-Control, ETag)
  • The Range request header, enabling resumable downloads
  • Chunked responses, useful for transferring large files
  • A mandatory Host header, making virtual hosting on the internet possible

HTTP/2.0

Persistent connections in HTTP/1.1 eliminated much of the cost of creating and tearing down connections, but its concurrency remained limited in two ways:

  • With a persistent connection, HTTP/1.1 can only process one request at a time per connection. Until the current request finishes, all other requests are stuck waiting — the situation known as head-of-line blocking
  • To ease server load, browsers cap the number of HTTP connections per domain, typically at 6 to 8

HTTP/2, officially released in 2015, dropped ASCII-encoded transmission by default in favor of binary data for better efficiency.

When sending requests, the client packages each request's content into numbered binary frames and sends them all to the server together. On receipt, the server reassembles frames with the same number into the complete request. The same framing and reassembly applies in the other direction, when the server returns results to the client.

With binary framing, a client needs only a single connection per domain to handle all its communication. Sending multiple requests over one connection this way is called multiplexing, and each of these lanes is called a stream.

The main changes in HTTP/2.0:

  • Data transmitted as a binary protocol rather than plain text
  • Multiplexing, replacing 1.1's pipelining
  • Header compression with a dedicated algorithm, reducing data volume
  • Frame prioritization, letting the server handle certain requests first
  • Server push, allowing the server to proactively send data to the client
  • All header fields lowercase; pseudo-headers introduced, appearing before regular header fields and starting with a colon
  • Stronger security, with encrypted communication (TLS) required "in practice"

Although HTTP/2.0 has been out for years, HTTP/1.1 has proven so entrenched and dominant that HTTP/2.0 adoption remains modest — plenty of websites still run HTTP/1.1.

HTTP/3.0

Of course, HTTP/2 isn't perfect either. If packets are lost during communication, or either side's network drops, the entire TCP connection stalls.

Because HTTP/2 multiplexes via binary framing, it typically uses just one TCP connection — so when packets are lost or the network hiccups, everything behind the loss gets blocked. HTTP/1.1, by contrast, can open multiple TCP connections; if one fails, the others carry on transferring data unaffected. In this scenario HTTP/2 actually performs worse than HTTP/1.

In 2018, HTTP/3 swapped the underlying TCP for UDP, eliminating the problem at its root. UDP's biggest difference from TCP is that it needs no connection setup to send data and can fire off multiple packets simultaneously — very efficient, but with no acknowledgment mechanism to guarantee delivery. So Google started from scratch and built the UDP-based QUIC protocol, which HTTP/3 adopted. Many browsers and hardware vendors now support HTTP/3.0.

The QUIC Protocol

Baidu Baike: QUIC_百度百科 (baidu.com)

QUIC sounds like "quick", and that's precisely the point — fast. It's a UDP-based transport protocol proposed by Google, hence its other name: "Quick UDP Internet Connections". QUIC's defining trait is speed. So why is it fast, and where exactly does the speed come from?

1) Faster Connection Establishment

As we know, HTTP uses TCP at the transport layer, and HTTPS and HTTP/2.0 add TLS for encryption on top — which means handshake-induced connection latency: the TCP three-way handshake (one RTT) plus the TLS handshake (two RTTs), as shown below.

For short-lived connections this handshake latency hurts, and it can't be eliminated. RTT is, after all, humanity's eternal battle with efficiency.

QUIC's handshake is faster because it uses UDP as its transport, cutting out the three-way-handshake delay. Its encryption uses the newest version of TLS, TLS 1.3. Compared with TLS 1.1–1.2, TLS 1.3 lets the client start sending application data without waiting for the TLS handshake to complete, supporting 1-RTT and even 0-RTT — achieving fast connection establishment.

2) No Head-of-Line Blocking

As noted earlier, HTTP/2.0 solved head-of-line blocking at the application layer, but since its connections still ride on TCP, blocking at the transport layer remained.

UDP has no notion of a connection to begin with, and QUIC's streams are isolated from one another — a stall in one stream never blocks the processing of data in others. So running on UDP means no head-of-line blocking.

3) Reliability and More Accurate RTT

TCP guarantees reliability with a sequence number + acknowledgment number mechanism: once a packet carrying a synchronize sequence number reaches the server, the server responds within a certain window; if that window passes with no response, the client retransmits the packet until the server receives it and responds.

So how does TCP decide its retransmission timeout?

TCP generally uses an adaptive retransmission algorithm, dynamically adjusting the timeout based on the round-trip time, RTT. But because retransmissions reuse the same sequence number, there's no way to tell whether a response corresponds to the original packet or the retransmitted one, making the computed RTT rather inaccurate.

QUIC doesn't use TCP, but it still guarantees reliability. Its mechanism is the Packet Number — a sequence number you can think of as the successor to the synchronize sequence number, and likewise monotonically increasing. Unlike syn, the Packet Number increments regardless of whether the server received the packet; syn only increments after the server sends an ack.

Say a packet with PN = 10 gets delayed on its way to the server for whatever reason; the client then retransmits it as PN = 11. When the client later receives the response for PN = 10 and replies in turn, the RTT measured is exactly the time that the PN = 10 packet spent alive in the network — a much more accurate calculation.

QUIC guarantees packet reliability, but how is data reliability guaranteed?

QUIC introduces the concept of a stream offset. A single stream can carry multiple stream offsets, and each stream offset is essentially the data identified by a PN. Even if the data identified by some PN is lost, the retransmission under PN + 1 still carries the data originally identified by that PN. Once all PN-identified data reaches the server, it gets reassembled — guaranteeing data reliability. Stream offsets arriving at the server are assembled in order, which guarantees data ordering as well.

4) Pluggable Congestion Control

As everyone knows, TCP's implementation lives in the operating system kernel — applications can only use it, not modify the kernel. With mobile and ever more devices coming online, performance has become a critical metric. Yet while mobile networks have advanced rapidly, end-user upgrades crawl — I still see machines in many places running XP, long after its development ceased. Server-side systems don't depend on users upgrading, but since OS upgrades touch low-level software and runtime libraries, they too tend to be conservative and slow.

A key trait of QUIC is pluggability — it can be updated and upgraded dynamically. QUIC implements congestion control at the application layer, needing no OS or kernel support. Switching congestion control algorithms just takes a reload on the server — no downtime, no restart.

5) Flow Control and Security

TCP implements flow control with a sliding window. QUIC implements flow control too, likewise using window updates (window_update) to tell the peer how many bytes it can accept.

TCP headers are neither encrypted nor authenticated, so they can easily be tampered with in transit. QUIC is different: its packet headers are authenticated and its payloads encrypted. Any modification to a QUIC packet is immediately detectable by the receiver, ensuring security.

6) QUIC's Advantages, Summed Up

All told, QUIC brings these advantages:

  • Uses UDP, so no three-way handshake to establish a connection, and shorter TLS setup time as well.
  • Solves head-of-line blocking.
  • Dynamically pluggable — congestion control lives at the application layer and can be swapped anytime.
  • Packet headers authenticated and payloads encrypted, ensuring security.
  • Connections migrate smoothly.

Smooth connection migration means that when your phone or mobile device switches between 4G and WiFi, the connection doesn't drop and reconnect — users notice nothing at all as the handover happens seamlessly. QUIC is now standardized in RFC 9000.

Wrapping Up

Looking back at HTTP's evolution: 0.9 could only transfer HTML text; 1.0 introduced headers and diversified what could be transferred; 1.1 used persistent connections to eliminate the cost of repeated connection setup; 2.0 broke through the concurrency ceiling with binary framing and multiplexing. But as long as TCP remained underneath, transport-layer head-of-line blocking could never be eradicated — so HTTP/3 switched to the UDP-based QUIC, redesigning connection establishment, reliable delivery, and congestion control at the application layer. Each generation of the protocol has paid down the debts of the one before it, and HTTP/3.0 will surely see use in more and more scenarios.

COMMENTS