Contents

HTTP/3 Benchmarks: QUIC Performance on Lossy Networks with Packet Analysis

HTTP/3 replaces TCP+TLS with QUIC, claiming to solve “head-of-line blocking”. But how does it actually perform? This post uses simulated degraded networks to compare HTTP/2 and HTTP/3 with real data.

1. HTTP/3 Core Improvements

1.1 Protocol Stack Comparison

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
HTTP/2:                    HTTP/3:
┌─────────────┐            ┌─────────────┐
│   HTTP/2    │            │   HTTP/3    │
├─────────────┤            ├─────────────┤
│    TLS 1.3  │            │    QUIC     │ ← Transport + encryption merged
├─────────────┤            │  (incl TLS) │
│     TCP     │            └──────┬──────┘
└─────────────┘                   │
                            ┌─────▼─────┐
                            │    UDP    │
                            └───────────┘

1.2 Theoretical Advantages

ProblemHTTP/2 (TCP)HTTP/3 (QUIC)
Handshake latencyTCP (1-RTT) + TLS (1-2 RTT)0-1 RTT
Head-of-line blockingExists at TCP layerCompletely solved
Connection migrationIP change requires reconnectSeamless migration
Packet loss recoverySlow retransmissionFaster recovery

2. Test Environment Setup

2.1 Simulating Degraded Networks

Using Linux tc command:

1
2
3
4
5
6
7
8
# Add 100ms delay + 5% packet loss
sudo tc qdisc add dev eth0 root netem delay 100ms loss 5%

# Check current settings
tc qdisc show dev eth0

# Restore normal
sudo tc qdisc del dev eth0 root

2.2 Test Targets

Pick a site that supports both HTTP/2 and HTTP/3:

1
2
3
# Check if site supports HTTP/3
curl -I https://cloudflare.com 2>&1 | grep -i alt-svc
# alt-svc: h3=":443"  ← Supports HTTP/3

2.3 curl with HTTP/3 Support

1
2
3
4
5
6
7
# Check curl version (need 7.66+)
curl --version | grep HTTP3

# If not supported, install HTTP/3-enabled version
# Ubuntu:
sudo add-apt-repository ppa:savoury1/curl34
sudo apt update && sudo apt install curl

3. Performance Comparison Tests

3.1 Normal Network

1
2
3
4
5
# HTTP/2
curl -w "Total: %{time_total}s\n" -o /dev/null -s --http2 https://cloudflare.com

# HTTP/3
curl -w "Total: %{time_total}s\n" -o /dev/null -s --http3 https://cloudflare.com

Results (no degradation):

ProtocolFirst ConnectionReused Connection
HTTP/2180ms50ms
HTTP/3150ms45ms

HTTP/3 is slightly faster, but not by much.

3.2 High Latency Network (100ms RTT)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Add delay
sudo tc qdisc add dev eth0 root netem delay 100ms

# Test
for i in {1..10}; do
  echo "=== HTTP/2 ==="
  curl -w "%{time_total}s\n" -o /dev/null -s --http2 https://cloudflare.com
  echo "=== HTTP/3 ==="
  curl -w "%{time_total}s\n" -o /dev/null -s --http3 https://cloudflare.com
done

Results (100ms RTT):

ProtocolAvg TimeHandshake %
HTTP/2650ms46%
HTTP/3450ms33%

Analysis: On high-latency networks, QUIC’s 0-RTT handshake advantage is significant.

3.3 Lossy Network (5% Packet Loss)

1
2
3
4
5
6
# Add packet loss
sudo tc qdisc change dev eth0 root netem delay 50ms loss 5%

# Test (load a large page)
time curl -o /dev/null -s --http2 https://www.cloudflare.com/
time curl -o /dev/null -s --http3 https://www.cloudflare.com/

Results (5% packet loss):

ProtocolAvg TimeStd Dev
HTTP/22.1s±0.8s
HTTP/31.3s±0.3s

Key findings:

  1. HTTP/3 is 40% faster on average
  2. HTTP/3 is more stable (lower standard deviation)

4. Packet Analysis

4.1 Capturing QUIC with Wireshark

1
2
3
4
5
6
7
# Need to set SSLKEYLOGFILE to decrypt QUIC
export SSLKEYLOGFILE=/tmp/keylog.txt
curl --http3 https://cloudflare.com

# In Wireshark:
# Edit → Preferences → Protocols → TLS
# Set (Pre)-Master-Secret log filename: /tmp/keylog.txt

4.2 Observing Head-of-Line Blocking

HTTP/2 (TCP) Problem:

1
2
3
4
5
6
7
8
9
Timeline:
  Stream 1: [Packet 1] [Packet 2] ...
  Stream 2: [Packet 1] [Packet 2] ...
  Stream 3: [Packet 1] [Packet 2] ...

If Stream 1's Packet 1 is lost:
  TCP: Wait for retransmission... (ALL streams are blocked!)
  Stream 2, 3 data has arrived, but can't be delivered to app

HTTP/3 (QUIC) Solution:

1
2
3
4
QUIC implements multiplexing over UDP:
  Stream 1: [Packet 1 lost] → Retransmit
  Stream 2: [Processing normally] ← Not affected!
  Stream 3: [Processing normally] ← Not affected!

4.3 Connection Migration Demo

QUIC uses Connection ID instead of IP:Port to identify connections:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Simulate WiFi → 4G switch
# 1. Start download
curl --http3 -O https://terra-nas.com/largefile.bin &

# 2. Switch network interface
sudo ip route change default via 192.168.1.1 dev wlan0
sudo ip route change default via 10.0.0.1 dev eth0

# 3. Observe if download is interrupted
# HTTP/3: Continues (Connection ID unchanged)
# HTTP/2: Connection drops, needs to reconnect

5. QUIC Internals Deep Dive

5.1 Flow Control

QUIC has two levels of flow control:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Connection level: Limits total flow across all Streams
Stream level: Limits individual Stream flow

┌─────────────────────────────────────┐
│ Connection Window = 1MB             │
│ ┌───────────┐ ┌───────────┐        │
│ │ Stream 1  │ │ Stream 2  │ ...    │
│ │ Window=   │ │ Window=   │        │
│ │ 256KB     │ │ 256KB     │        │
│ └───────────┘ └───────────┘        │
└─────────────────────────────────────┘

5.2 Packet Loss Detection & Recovery

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TCP packet loss recovery:
  Send: 1, 2, 3, 4, 5
  ACK:  1, 2, -  (3 lost)
  Wait for timeout... or 3 duplicate ACKs
  Retransmit: 3

QUIC packet loss recovery:
  Each packet has a unique, incrementing sequence number
  ACK packets contain detailed received ranges
  More accurate RTT estimation
  Faster retransmission triggering

5.3 0-RTT Mechanism

1
2
3
4
5
6
7
8
First connection:
  Client → Server: ClientHello + supported keys
  Client ← Server: ServerHello + cert + Session Ticket
  Client → Server: Start sending encrypted data

Subsequent connections:
  Client → Server: ClientHello + EarlyData (using previous Session Ticket)
  Data is already transmitting! No need to wait for handshake

6. Practical Recommendations

6.1 When to Use HTTP/3

ScenarioRecommendationReason
Mobile clientsStrongly recommendedFrequent network switching, high packet loss
Weak network regionsStrongly recommendedHigh latency, high packet loss
Internal servicesOptionalStable network, small benefit
Real-time communicationRecommendedLow latency requirements

6.2 Nginx HTTP/3 Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Requires Nginx 1.25+ or nginx-quic
server {
    listen 443 ssl;
    listen 443 quic reuseport;
    
    http2 on;
    http3 on;
    http3_hq on;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # Tell clients HTTP/3 is supported
    add_header Alt-Svc 'h3=":443"; ma=86400';
}

6.3 CDN Support Status

CDNHTTP/3 Support
CloudflareEnabled by default
Google Cloud CDNSupported
AWS CloudFrontSupported
AkamaiSupported

7. Summary

7.1 Test Conclusions

Network ConditionHTTP/3 Advantage
Normal networkSlightly faster 10-20%
High latency (100ms+)30-40% faster
High packet loss (5%+)40%+ faster, more stable
Network switchingSeamless migration vs reconnect

7.2 Key Takeaways

  1. Head-of-line blocking is a real problem: On lossy networks, TCP-multiplexed streams block each other
  2. 0-RTT is a killer feature: Noticeably better on high-latency networks
  3. UDP ≠ unreliable: QUIC implements reliable transport in userspace
  4. Low migration cost: Major browsers and CDNs already support it, just flip a config switch

Recommendation: If your user base has a significant mobile or weak-network population, HTTP/3 is worth enabling.