Skip to main content
โšก Calmops

HTTP/3 and QUIC: The Future of Transport Protocols

For decades, HTTP has evolved from a simple request-response protocol to the backbone of the modern web. HTTP/1.0 gave us the foundation, HTTP/1.1 brought persistent connections, HTTP/2 introduced multiplexing, and now HTTP/3 promises to revolutionize how we transmit data over the internet.

But HTTP/3 isn’t just another incremental improvementโ€”it’s a complete reimagining of how web traffic moves. Built on QUIC (Quick UDP Internet Connections), it addresses fundamental limitations that have plagued HTTP/2 and its predecessors.

In this guide, we’ll explore the evolution from HTTP/1.1 to HTTP/3, understand why QUIC was necessary, and learn how to leverage these new protocols in your applications.

The Evolution of HTTP

HTTP/1.1: The Foundation

HTTP/1.1 introduced persistent connections, eliminating the overhead of establishing a new TCP connection for each request. However, it suffered from a critical limitation: head-of-line blocking.

# HTTP/1.1 Limitations
problems:
  - Head-of-line blocking: requests must wait for previous ones
  - Single request per connection
  - No multiplexing
  - Inefficient header compression
  - Plain text (later fixed with TLS)

HTTP/2: Multiplexing and More

HTTP/2 introduced binary framing, multiplexing, header compression (HPACK), and server push. Finally, we could send multiple requests concurrently over a single connection.

# HTTP/2 Connection
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚            TCP Connection               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚        Stream 1 (GET /a)        โ”‚   โ”‚
โ”‚  โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  โ”‚   โ”‚
โ”‚  โ”‚        Stream 2 (GET /b)        โ”‚   โ”‚
โ”‚  โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€  โ”‚   โ”‚
โ”‚  โ”‚        Stream 3 (GET /c)        โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

However, HTTP/2 still relies on TCP, which means head-of-line blocking persists at the transport layer.

# HTTP/2 Head-of-Line Blocking Problem
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              TCP Stream                 โ”‚
โ”‚                                         โ”‚
โ”‚  [Request A] โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ (waiting)    โ”‚
โ”‚  [Request B] โ–ˆโ–ˆโ–ˆโ–ˆ                        โ”‚
โ”‚  [Request C] โ–ˆโ–ˆ                          โ”‚
โ”‚                                         โ”‚
โ”‚  If packet for A is lost,               โ”‚
โ”‚  B and C must wait!                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

HTTP/3: Breaking the TCP Barrier

HTTP/3 moves away from TCP entirely, using QUICโ€”a UDP-based protocolโ€”to eliminate head-of-line blocking at the transport layer.

# HTTP/3 Connection
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              QUIC Stream                โ”‚
โ”‚                                         โ”‚
โ”‚  Stream 1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ   โ”‚
โ”‚  Stream 2 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ   โ”‚
โ”‚  Stream 3 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ   โ”‚
โ”‚                                         โ”‚
โ”‚  Independent delivery - no blocking!   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Understanding QUIC

QUIC was developed by Google and later standardized by IETF (RFC 9000). It runs over UDP instead of TCP, giving it unique advantages.

Why UDP?

TCP provides reliability but at a cost: strict ordering and connection-oriented behavior. UDP is connectionless and doesn’t guarantee delivery or orderโ€”features TCP uses to ensure reliability.

# TCP vs UDP characteristics
tcp_characteristics = {
    "connection": "stateful",
    "ordering": "guaranteed",
    "delivery": "reliable",
    "flow_control": "yes",
    "congestion_control": "yes"
}

udp_characteristics = {
    "connection": "stateless", 
    "ordering": "not guaranteed",
    "delivery": "best effort",
    "flow_control": "no",
    "congestion_control": "no"
}

QUIC builds reliability, ordering, and congestion control on top of UDPโ€”getting the best of both worlds.

QUIC Packet Structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ QUIC Packet                                                โ”‚
โ”‚                                                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ Headerโ”‚ Long/  โ”‚  Connection  โ”‚    Frames         โ”‚   โ”‚
โ”‚  โ”‚ Flags โ”‚ Form   โ”‚    ID        โ”‚  (Crypto, STREAM, โ”‚   โ”‚
โ”‚  โ”‚       โ”‚        โ”‚              โ”‚   ACK, etc.)      โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                            โ”‚
โ”‚  All fields are encrypted!                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key QUIC Features

1. Connection Establishment

TCP requires three-way handshake (1-RTT), then TLS handshake (1-2 RTTs). QUIC combines both, achieving connection establishment in just 1-RTTโ€”or even 0-RTT for previously connected clients.

# Connection establishment comparison

# TCP + TLS
rtt_to_connect = "3 RTT (TCP handshake + TLS)"
#   C โ†’ S: SYN
#   S โ†’ C: SYN-ACK
#   C โ†’ S: ACK + TLS ClientHello
#   S โ†’ C: TLS ServerHello + Certificate
#   C โ†’ S: TLS Finished
#   NOW WE CAN SEND DATA

# QUIC
rtt_to_connect = "1 RTT (or for returning 0-RTT clients)"
#   C โ†’ S: ClientHello + Crypto (Encrypts 0-RTT data)
#   S โ†’ C: ServerHello + Crypto + Finished
#   NOW WE CAN SEND DATA

2. 0-RTT Reconnection

When a client has previously connected to a server, it can send data immediately on reconnection without waiting for any handshake round-trips.

# 0-RTT Example
client_previous_state = {
    "connection_id": "0x12345678",
    "crypto_keys": "derived_previous_keys"
}

def quick_reconnect(server_address):
    # Client immediately sends encrypted data
    packet = encrypt_with_previous_keys({
        "data": "GET /api/users HTTP/3",
        "connection_id": "0x12345678"
    })
    
    # Server can decrypt and process immediately!
    return send_to_server(packet)

This is particularly valuable for:

  • Mobile networks with frequent reconnections
  • Resumable sessions
  • Reducing perceived latency

3. Connection Migration

TCP connections are bound to a 4-tuple (source IP, source port, dest IP, dest port). When your network changes (WiFi to cellular, for example), the connection breaks.

QUIC uses connection IDs that remain stable even when IP addresses change.

# Connection Migration Example

# Before network change (WiFi)
connection_on_wifi = {
    "connection_id": "abc123",
    "local_ip": "192.168.1.100",
    "local_port": 443,
    "server_ip": "1.2.3.4"
}

# After switching to cellular (different IP)
connection_on_cellular = {
    "connection_id": "abc123",  # Same!
    "local_ip": "10.20.30.40",  # Different
    "local_port": 443,
    "server_ip": "1.2.3.4"
}

# QUIC detects this and seamlessly continues!
# No connection timeout, no reconnection

4. Stream Multiplexing

Each stream in QUIC is independent. If one packet is lost in stream 1, streams 2, 3, and 4 continue without interruption.

# QUIC Stream Independence
independent_streams = {
    "stream_1": {"status": "blocked (packet lost)", "waiting": true},
    "stream_2": {"status": "delivered", "waiting": false},
    "stream_3": {"status": "delivered", "waiting": false},
    "stream_4": {"status": "in-flight", "waiting": false}
}

# Only stream 1 is blocked - others continue!

5. Built-in TLS 1.3

QUIC requires TLS 1.3 and encrypts almost everythingโ€”including the connection ID and packet numbers.

# QUIC encryption levels

# Initial keys (from handshake)
initial_keys = "encrypts early data"

# Handshake keys (1-RTT)
handshake_keys = "protects handshake"

# Application keys (1-RTT)
application_keys = "encrypts all application data"

# Even packet numbers are encrypted
# Prevents packet number analysis attacks

Header Handling

QUIC headers are complex but efficient. They come in two forms:

Short Header (for established connections):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Header โ”‚ Packet โ”‚ Connection โ”‚  Packet    โ”‚
โ”‚ Form   โ”‚  Num   โ”‚    ID      โ”‚  Number    โ”‚
โ”‚  (1)   โ”‚ (1-4)  โ”‚  (0 or 4)  โ”‚  (1-4)     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Long Header (for handshake):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Header โ”‚ Versionโ”‚ DCID  โ”‚   SCID    โ”‚ Packet โ”‚  Length     โ”‚
โ”‚ Form   โ”‚ (4)    โ”‚ Len   โ”‚   Len     โ”‚  Num   โ”‚  +  Packet  โ”‚
โ”‚ (1)    โ”‚        โ”‚ (1)   โ”‚   (1)     โ”‚ (1-4)  โ”‚   Numbers   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

HTTP/3 Protocol Details

HTTP/3 uses QUIC streams to send requests and responses. Unlike HTTP/2’s binary framing, HTTP/3 uses QPACK for header compression.

Request/Response Flow

# HTTP/3 Request
Client                                          Server
  โ”‚                                                โ”‚
  โ”‚โ”€โ”€โ”€โ”€ HTTP/3 Request (Stream 1) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚
  โ”‚     [Headers: GET /index.html]                โ”‚
  โ”‚                                                โ”‚
  โ”‚โ—„โ”€โ”€โ”€ HTTP/3 Response (Stream 1) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
  โ”‚     [Headers: 200 OK] [Body: HTML]            โ”‚
  โ”‚                                                โ”‚
  โ”‚โ”€โ”€โ”€โ”€ HTTP/3 Request (Stream 3) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚
  โ”‚     [Headers: GET /styles.css]                 โ”‚
  โ”‚                                                โ”‚
  โ”‚โ—„โ”€โ”€โ”€ HTTP/3 Response (Stream 3) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
  โ”‚     [Headers: 200 OK] [Body: CSS]             โ”‚

QPACK Header Compression

QPACK is similar to HTTP/2’s HPACK but with a key difference: it handles the loss of header blocks gracefully.

# QPACK vs HPACK

# HPACK: If a header block is lost, all subsequent 
# headers on that stream are blocked until retransmission

# QPACK: Uses dynamic tables per-stream, can be 
# processed out of order when possible

qpack_features = {
    "dynamic_tables": "per-stream, not global",
    "insertion": "reference-based",
    "blocked_streams": "minimal due to independent streams"
}

HTTP/3 Frame Types

HTTP/3 defines several frame types:

http3_frames = {
    "DATA": "Body content",
    "HEADERS": "Compressed headers",
    "CANCEL_PUSH": "Cancel server push",
    "SETTINGS": "Connection parameters",
    "PUSH_PUSH": "Server push (limited in HTTP/3)",
    "GOAWAY": "Graceful shutdown",
    "MAX_PUSH_ID": "Limit server pushes"
}

Server Configuration

Nginx Configuration for HTTP/3

server {
    listen 443 ssl http3;
    listen [::]:443 ssl http3;
    
    ssl_protocols TLSv1.3;
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # Optional: HTTP/2 fallback
    listen 443 ssl http2;
    
    location / {
        root /var/www/html;
        index index.html;
    }
}

Caddy Configuration

# Caddyfile
:443 {
    encode zstd gzip
    
    # HTTP/3 enabled by default
    tls cert.pem key.pem
    
    respond "Hello, HTTP/3!" {
        close
    }
}

Apache Configuration

<VirtualHost *:443>
    Protocols h2 http/1.1 h3
    
    SSLEngine on
    SSLCertificateFile /path/to/cert.pem
    SSLCertificateKeyFile /path/to/key.pem
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.2 +TLSv1.3
</VirtualHost>

Client Implementation

Using curl with HTTP/3

# Check if curl supports HTTP/3
curl --version | grep http3

# Use HTTP/3 (if supported)
curl -v --http3 https://example.com

# Prefer HTTP/3, fallback to HTTP/2
curl -v --http2 https://example.com

Python httpx Client

import httpx

# HTTP/2 client
client = httpx.Client(http2=True)
response = client.get('https://example.com')

# For HTTP/3, use a QUIC-enabled transport
# (Requires quart or aioquic)
async def fetch_http3():
    async with httpx.AsyncClient() as client:
        # Some transports support HTTP/3
        response = await client.get('https://example.com')
        return response

JavaScript Fetch API

// Modern browsers support HTTP/3 automatically
// No special code needed - browser negotiates best protocol

async function fetchPage() {
    const response = await fetch('https://example.com/api/data');
    const data = await response.json();
    return data;
}

// Check negotiated protocol
console.log(response.protocol); // "h3", "h2", or "http/1.1"

Performance Comparison

Latency Measurements

# Typical latency improvements

protocol_latency = {
    "http1_1_pipelining": {
        "ttfb": "50-100ms",
        "scenario": "Multiple requests"
    },
    "http2": {
        "ttfb": "30-60ms",
        "scenario": "Multiple requests",
        "improvement": "50% vs HTTP/1.1"
    },
    "http3": {
        "ttfb": "20-40ms",
        "scenario": "First connection",
        "improvement": "30% vs HTTP/2"
    },
    "http3_0rtt": {
        "ttfb": "10-20ms",
        "scenario": "Reconnection",
        "improvement": "60% vs HTTP/2"
    }
}

Real-World Scenarios

# When HTTP/3 shines

scenarios = {
    "mobile_networks": {
        "benefit": "Connection migration",
        "improvement": "Seamless WiFi/cellular handoff"
    },
    "packet_loss": {
        "benefit": "No HOL blocking",
        "improvement": "40-50% faster under 2% loss"
    },
    "reconnection": {
        "benefit": "0-RTT",
        "improvement": "Instant resumption"
    },
    "cdn": {
        "benefit": "Faster TLS",
        "improvement": "Reduced TTFB"
    }
}

Browser Support

As of 2025, major browsers have HTTP/3 support:

// Check HTTP/3 support in browser
const supportsHTTP3 = (() => {
    const connection = navigator.connection || 
        navigator.mozConnection || 
        navigator.webkitConnection;
    return connection && connection.protocol === 'h3';
})();

// Alternative: Check via HTTPS
fetch('https://example.com', { method: 'HEAD' })
    .then(r => console.log(r.headers.get('alt-svc')))
    .catch(console.error);

Debugging HTTP/3

Using Chrome DevTools

// Network tab shows protocol
// Look for "h3" or "http/3" in protocol column

// QUIC internals: chrome://net-internals/#quic

Wireshark QUIC Dissector

# Enable QUIC decryption in Wireshark
# Edit โ†’ Preferences โ†’ Protocols โ†’ QUIC
# Add TLS master keys if available

# Filter QUIC traffic
quic.frame_type == 0x06  # CRYPTO frames
quic.packet_number == 1  # Specific packet

curl Verbose Output

# See protocol negotiation
curl -v https://example.com

# Output includes:
# *  SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305
# *  ALPN: h3, h2, http/1.1
# *  h3 (RFC 9114)

Migration Considerations

Moving to HTTP/3

# Checklist for HTTP/3 deployment

prerequisites:
  - TLS 1.3 support on server
  - Modern load balancers (HTTP/3 aware)
  - CDN with HTTP/3 support
  - Updated monitoring for new metrics

risks:
  - Some middleboxes still block UDP 443
  - Load balancer configuration complexity
  - Different debugging approach needed

recommendations:
  - Enable HTTP/3 but don't require it
  - Monitor "http3_fallback" metrics
  - Test with curl --http3

Fallback Strategy

# Protocol fallback order
fallback_chain = [
    "h3",           # HTTP/3 (preferred)
    "h2",           # HTTP/2 over TLS
    "http/1.1"     # HTTP/1.1 (fallback)
]

# Alt-Svc header tells client about alternatives
alt_svc_header = "h3=\":443\"; ma=3600"
# "Offer HTTP/3 on port 443 for 1 hour"

Conclusion

HTTP/3 represents a fundamental shift in how we transport web content. By moving from TCP to QUIC (UDP), it eliminates head-of-line blocking, reduces connection establishment latency, and enables seamless connection migration.

Key takeaways:

  • QUIC provides 1-RTT (or 0-RTT) connections with built-in encryption
  • HTTP/3 leverages QUIC streams for independent, non-blocking requests
  • Connection migration solves the mobile network handoff problem
  • 0-RTT enables instant reconnection for returning clients

While adoption is still growing, HTTP/3 is the future of web communication. Major CDNs and browsers already support it, making it a viable option for modern applications.


Comments