Table of Contents
1211 words
6 minutes
Deep Dive into HTTP/3 and the QUIC Protocol

Introduction: The Evolutionary Path of the HTTP Protocol#

From Tim Berners-Lee’s proposal of HTTP/0.9 in 1991 to today, the HTTP protocol has undergone four major iterations. Each upgrade has addressed the core pain points of its predecessor:

  • HTTP/1.0 (1996): Introduced request headers and response headers, supported content negotiation
  • HTTP/1.1 (1997): Persistent connections, pipelining, chunked transfer
  • HTTP/2 (2015): Binary framing, multiplexing, header compression, server push
  • HTTP/3 (2022): Built on QUIC, completely eliminates transport-layer head-of-line blocking

This article focuses on HTTP/3 and its underlying transport protocol QUIC, providing an in-depth analysis of its design motivation, core principles, and practical applications.

The Achilles’ Heel of HTTP/2#

HTTP/2 solved the application-layer head-of-line blocking problem of HTTP/1.1 through Multiplexing—multiple requests can be interleaved on the same TCP connection without waiting for previous requests to complete.

However, HTTP/2 still runs on top of TCP, and TCP is a strictly ordered byte-stream protocol. This means:

TCP connection
├─ Stream 1: [Frame A] ─────────── [Frame B] ── ✓
├─ Stream 2: ── [Frame C] ── [Frame D] ───────── ✗ (lost)
└─ Stream 3: ────────── [Frame E] ────────── ⏳ (blocked)

If a TCP packet from Stream 2 is lost, TCP must wait for its retransmission to succeed. During this period, all data from Stream 1 and Stream 3 cannot be processed by the upper layers, even if their data has already arrived completely. This is transport-layer head-of-line blocking (TCP Head-of-Line Blocking).

In high packet-loss network environments (such as mobile networks), HTTP/2 performance may degrade to worse than HTTP/1.1—because HTTP/1.1 typically opens multiple TCP connections, and packet loss on one connection does not block the others.

QUIC: Rebuilding the Transport Layer on Top of UDP#

QUIC (Quick UDP Internet Connections) was originally designed by Google, announced in 2013, and after nearly a decade of evolution became an IETF standard in 2021 (RFC 9000). Its core idea is: since TCP’s implementation is deeply embedded in the operating system kernel and difficult to iterate quickly, it’s better to build a modern transport-layer protocol on top of UDP in user space.

Key Design of QUIC#

FeatureTCPQUIC
Handshake latency1-3 RTT (including TLS)0-1 RTT
Head-of-line blockingCross-stream blockingIndependent within streams
Connection migrationNot supportedSupported (Connection ID)
Loss recoveryIn-order acknowledgmentOut-of-order acknowledgment (more precise RTT)
DeploymentKernel spaceUser space
EncryptionOptional (TLS layered on top)Built-in (always encrypted)

0-RTT Connection Establishment#

QUIC merges the transport handshake and the TLS 1.3 encryption handshake into a single round trip:

Traditional TCP + TLS 1.3:
Client ──SYN──────────────→ Server
Client ←──SYN-ACK─────────→ Server (1-RTT)
Client ──ClientHello──────→ Server
Client ←──ServerHello+Done→ Server (2-RTT)
Client ──HTTP Request─────→ (data sent only at 3-RTT)
QUIC:
Client ──ClientHello──────→ Server
Client ←──ServerHello────── Server (1-RTT)
Client ──HTTP Request─────→ (data can be sent at 1-RTT)
QUIC 0-RTT (connection resumption):
Client ──0-RTT data───────→ (0-RTT!)

Below is an example of creating a QUIC server with Node.js (based on Node.js’s built-in node:quic module):

import { createQuicSocket } from 'node:quic';
import { readFileSync } from 'node:fs';
const key = readFileSync('./server.key');
const cert = readFileSync('./server.cert');
const socket = createQuicSocket({ endpoint: { port: 4433 } });
socket.listen({
key,
cert,
alpn: 'h3', //ALPN identifier for HTTP/3
});
socket.on('session', (session) => {
session.on('stream', (stream) => {
//Handle HTTP/3 request streams
stream.on('data', (chunk) => {
console.log('Received request:', chunk.toString());
});
stream.end(
'HTTP/3 200 OK\r\n' +
'content-type: text/plain\r\n' +
'\r\n' +
'Hello HTTP/3!'
);
});
});
console.log('QUIC server listening on port 4433');

Core Improvements of HTTP/3#

HTTP/3 (RFC 9114) is an HTTP protocol mapping that runs on top of QUIC. It inherits HTTP/2’s semantic model (methods, status codes, header fields) but makes fundamental adjustments to the transport mechanism.

1. Multiplexing Based on QUIC Streams#

HTTP/3 maps each request-response pair to a QUIC Stream. Streams are completely independent of each other—packet loss in a single Stream only affects that Stream itself and does not block other Streams:

QUIC connection
├─ Stream 1 (Request A): [Frame 1] ── [Frame 2] ── ✓
├─ Stream 2 (Request B): [Frame 3] ── [Frame 4] ── ✗ packet loss, waiting for retransmission
└─ Stream 3 (Request C): [Frame 5] ── [Frame 6] ── ✓ unaffected!

2. QPACK Header Compression#

HTTP/2 uses HPACK for header compression, but HPACK relies on TCP’s ordered delivery to maintain encoding table synchronization. HTTP/3 redesigned QPACK, which uses two independent unidirectional streams to manage the dynamic table, allowing the encoding table to be updated without blocking request processing.

QPACK's bidirectional table synchronization:
┌─────────────────────────┐
│ Encoder stream (unidirectional) │ ← Send table update instructions
├─────────────────────────┤
│ Decoder stream (unidirectional) │ ← Acknowledge that table updates have been received
└─────────────────────────┘
Request stream / Response stream (bidirectional) ← Carry compressed headers

3. Connection Migration#

QUIC uses a Connection ID rather than a four-tuple (source IP, source port, destination IP, destination port) to identify a connection. This means that when a client switches from Wi-Fi to a cellular network, the connection will not be interrupted:

Wi-Fi network (192.168.1.100) ─────── Connection ID: 0xABCD
↓ Network switch
Cellular network (10.0.0.55) ─────── Still Connection ID: 0xABCD
Server seamlessly identifies!

This is the most significant experience improvement in mobile scenarios—video calls and real-time applications no longer disconnect due to network switching.

Hands-On: Testing HTTP/3 with curl#

Since 2024, mainstream browsers (Chrome, Firefox, Safari) and web servers (Nginx, Caddy, Cloudflare) have all supported HTTP/3. You can use the following tools to verify whether a site supports it:

Terminal window
#curl must be built with HTTP/3 support
#Use the --http3 flag to send requests
curl -I --http3 https://cloudflare-quic.com
#Example output:
# HTTP/3 200
# content-type: text/html
# server: cloudflare
# alt-svc: h3=":443"; ma=86400

You can also visually observe HTTP/3 using browser DevTools:

  1. Open Chrome DevTools → Network panel
  2. Right-click on the table header → check the Protocol column
  3. Visit https://www.google.com, and the Protocol column will show h3

Enabling HTTP/3 in Nginx#

Nginx has provided experimental support for HTTP/3 since 1.25.0. Below is a minimal configuration:

server {
#QUIC and HTTP/3 listening
listen 443 quic reuseport;
#Also compatible with HTTP/2 (over TCP)
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
#Enable HTTP/3
http3 on;
#Inform clients via the Alt-Svc header that this service supports h3
add_header Alt-Svc 'h3=":443"; ma=86400';
location / {
#General configuration
root /var/www/html;
}
}

The Alt-Svc response header in the configuration is key—it tells the browser “this website can also be accessed via HTTP/3 (h3),” and the browser will prioritize HTTP/3 in subsequent requests.

Performance Comparison: HTTP/2 vs HTTP/3#

Google’s real-world data shows that after deploying QUIC on YouTube:

  • Video buffering rate decreased by approximately 30%
  • Average desktop loading latency was reduced by approximately 8%
  • Mobile loading latency improved even more significantly in high packet-loss scenarios (over 15%)

These gains mainly come from:

  1. 0-RTT connection resumption (extremely friendly for repeat visits)
  2. Elimination of TCP head-of-line blocking (particularly effective in high packet-loss mobile networks)
  3. Better congestion control (QUIC’s BBR algorithm adapts better to modern networks than traditional TCP Cubic)

Summary#

The core contributions of HTTP/3 and QUIC lie in two things: solving transport-layer head-of-line blocking with multiple independent streams, and achieving seamless connection migration with Connection IDs. They are not a complete rebuild—HTTP’s semantic layer remains unchanged, and QPACK inherits HPACK’s header compression ideas—but rather a precise surgery on the “waist of the protocol stack.”

If HTTP/2 let us transmit concurrently over a single TCP connection, then HTTP/3 gives each data stream its own independent “virtual connection.” For today’s mobile-first, high-latency, jittery internet, this is a fundamentally correct direction.

Currently, HTTP/3’s global deployment rate has exceeded 40% (according to W3Techs 2026 data), and mainstream CDNs and cloud providers have all enabled it by default. If your service hasn’t adopted HTTP/3 yet, now is the best time.


References:

Deep Dive into HTTP/3 and the QUIC Protocol
https://www.hehonglei.com/posts/http3-quic-deep-dive/
Author
HHLBlog
Published at
2026-08-12
License
CC BY-NC-SA 4.0