# Key Differences Between HTTP/1.1, HTTP/2, and HTTP/3: Complete Protocol Comparison

> Explore key differences between HTTP/1.1, HTTP/2, and HTTP/3. Understand protocol evolution from TCP to QUIC, binary framing, and multiplexing for faster web performance.

- Repository: [CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes)
- Tags: deep-dive
- Published: 2026-02-24

---

**HTTP/1.1 relies on text-based messaging over TCP with sequential request processing, HTTP/2 introduces binary framing and multiplexing over TCP, and HTTP/3 replaces TCP entirely with QUIC over UDP to eliminate head-of-line blocking and reduce connection latency.**

The HyperText Transfer Protocol has evolved through three major architectural revisions to address the performance bottlenecks of modern web applications. Understanding the differences between HTTP/1.1, HTTP/2, and HTTP/3 is critical for optimizing network stack implementations, as detailed in the `CyC2018/CS-Notes` repository's networking documentation.

## Transport Layer and Connection Architecture

The fundamental distinction between these protocols lies in their transport layer dependencies.

**HTTP/1.1** operates exclusively over **TCP**, establishing persistent connections that allow multiple requests over a single connection, but still requiring new TCP handshakes for each distinct host. According to [`notes/HTTP.md`](https://github.com/CyC2018/CS-Notes/blob/main/notes/HTTP.md) in the CS-Notes repository, this version relies on the standard three-way TCP handshake for every connection initialization.

**HTTP/2** maintains TCP as the underlying transport but implements a single TCP connection with multiple concurrent **streams**. This reduces handshake overhead while maintaining reliable, ordered delivery through TCP's congestion control mechanisms.

**HTTP/3** abandons TCP entirely in favor of **QUIC**, which runs over **UDP**. As implemented in the protocol specifications referenced in [`notes/HTTP.md`](https://github.com/CyC2018/CS-Notes/blob/main/notes/HTTP.md), QUIC incorporates **TLS 1.3** directly into its handshake mechanism, reducing round trips and enabling connection migration across network changes without interruption.

## Message Framing and Head-of-Line Blocking

Each protocol handles message serialization differently, directly impacting latency characteristics.

**HTTP/1.1** uses **text-based framing** consisting of a start line, headers, a blank line, and an optional body. This human-readable format introduces parsing overhead and suffers from **head-of-line blocking** at the application layer—if one request stalls, all subsequent requests on the same TCP connection must wait.

**HTTP/2** transitions to **binary framing** with distinct frame types including `HEADERS`, `DATA`, and `SETTINGS` frames. While this eliminates application-layer head-of-line blocking through multiplexing, TCP-level blocking persists because packet loss in the underlying TCP stream stalls all multiplexed streams.

**HTTP/3** implements binary frames over **QUIC streams**, which are independent and unordered. Because QUIC handles packet loss at the stream level rather than connection level, a lost packet affects only the specific stream, not all concurrent streams, as illustrated in `notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png`.

## Header Compression Mechanisms

Modern web applications generate numerous small requests, making header compression critical for bandwidth efficiency.

**HTTP/1.1** transmits headers as **plain text** with every request, creating significant overhead when sending repetitive metadata like cookies and user agent strings.

**HTTP/2** introduces **HPACK** compression, utilizing static and dynamic tables to eliminate duplication. The CS-Notes repository references this in `notes/pics/_u4E0B_u8F7D.png`, which illustrates how HPACK maintains stateful compression contexts between endpoints.

**HTTP/3** employs **QPACK**, an adaptation of HPACK designed for QUIC's out-of-order delivery model. QPACK avoids head-of-line blocking in the compression layer that could occur when HPACK updates are delayed due to packet loss.

## Server Push and Prioritization

Advanced features vary significantly across versions.

**HTTP/1.1** provides **no native server push** capability; clients must explicitly request every resource.

**HTTP/2** implements server push through `PUSH_PROMISE` frames, allowing servers to preemptively send resources before the client requests them. The mechanism is visualized in `notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png` from the CS-Notes repository.

**HTTP/3** maintains equivalent push functionality but leverages QUIC's stream prioritization, which implements dependency and weight signaling with improved loss-recovery semantics compared to HTTP/2's prioritization scheme.

## Detecting Protocol Versions in Practice

You can verify which protocol version your client is using through standard command-line tools and browser developer tools.

### Curl Protocol Verification

Use the `--http1.1`, `--http2`, and `--http3` flags to force specific versions and observe the negotiated protocol in verbose output:

```bash

# Force HTTP/1.1

curl -v --http1.1 https://example.com

# Force HTTP/2 (requires nghttp2 support)

curl -v --http2 https://example.com

# Force HTTP/3 (requires quiche or lsquic support)

curl -v --http3 https://example.com

```

The `-v` flag reveals the negotiated protocol in the response headers.

### Node.js Implementation Examples

Different modules handle each protocol version:

```javascript
// HTTP/1.1 (standard module)
const http = require('http');
http.get('http://example.com', res => {
  console.log('HTTP/1.1 status:', res.statusCode);
});

// HTTP/2 (built-in http2 module)
const http2 = require('http2');
const client = http2.connect('https://example.com');
const req = client.request({ ':path': '/' });
req.on('response', (headers) => {
  console.log('HTTP/2 status:', headers[':status']);
});
req.end();

// HTTP/3 (experimental, Node.js >= v22)
const { createQuicSocket } = require('net');
(async () => {
  const socket = createQuicSocket({ 
    endpoint: { address: 'example.com', port: 443 } 
  });
  const stream = await socket.openStream();
  stream.write('GET / HTTP/3\r\n\r\n');
  stream.on('data', (data) => console.log(data.toString()));
})();

```

### Browser Developer Tools

In Chrome DevTools, open the **Network** panel and examine the **Protocol** column. Entries display `h2` for HTTP/2 and `h3` for HTTP/3, while HTTP/1.1 appears as `http/1.1`.

## Summary

- **Transport**: HTTP/1.1 and HTTP/2 use TCP; HTTP/3 uses QUIC over UDP with integrated TLS 1.3
- **Framing**: HTTP/1.1 uses text; HTTP/2 and HTTP/3 use binary frames
- **Multiplexing**: HTTP/1.1 allows one request per connection; HTTP/2 multiplexes over TCP; HTTP/3 eliminates TCP head-of-line blocking entirely
- **Compression**: HTTP/1.1 has none; HTTP/2 uses HPACK; HTTP/3 uses QPACK optimized for QUIC streams
- **Connection Setup**: HTTP/3 reduces latency through combined handshake and supports connection migration across networks

## Frequently Asked Questions

### What is the main advantage of HTTP/3 over HTTP/2?

HTTP/3 eliminates **TCP head-of-line blocking** by using QUIC over UDP. While HTTP/2 multiplexes streams over a single TCP connection, any packet loss in TCP stalls all streams. QUIC handles packet loss at the individual stream level, allowing other streams to continue uninterrupted.

### Why does HTTP/2 still suffer from head-of-line blocking if it supports multiplexing?

HTTP/2 multiplexes multiple streams over a **single TCP connection**. Because TCP guarantees ordered delivery of bytes, if a packet is lost, TCP pauses the entire connection until retransmission succeeds. This stalls all HTTP/2 streams, even those whose data arrived successfully. The [`notes/HTTP.md`](https://github.com/CyC2018/CS-Notes/blob/main/notes/HTTP.md) file in CS-Notes explains this architectural limitation.

### Is HTTP/3 backward compatible with HTTP/1.1 and HTTP/2?

HTTP/3 is **not directly backward compatible** at the transport layer because it requires QUIC/UDP support rather than TCP. However, through **Application-Layer Protocol Negotiation (ALPN)**, clients and servers can fall back to HTTP/2 or HTTP/1.1 if QUIC connectivity fails, ensuring interoperability.

### When should developers choose HPACK versus QPACK?

You use **HPACK** with HTTP/2 over TCP connections, where the ordered stream guarantees that encoder and decoder contexts remain synchronized. You use **QPACK** with HTTP/3 over QUIC, where independent streams require special handling to prevent head-of-line blocking in the compression layer due to out-of-order packet delivery.