Skip to main content

HTTP/2 & HTTP/3: Multiplexing and QUIC

HTTP/2 processes multiple requests simultaneously over a single connection through multiplexing, improving page load performance by 2–3× compared to HTTP/1.1. HTTP/3 uses the UDP-based QUIC protocol to maintain performance even in high packet-loss environments.


HTTP/1.1 vs HTTP/2 vs HTTP/3

HTTP Version Comparison

ItemHTTP/1.1HTTP/2HTTP/3
TransportTCPTCPUDP (QUIC)
Multiplexing❌ (HOL blocking)✅ (single TCP stream)✅ (independent QUIC streams)
Header compression✅ (HPACK)✅ (QPACK)
Server Push✅ (limited practical use)❌ (removed)
TLS requiredDe facto mandatory✅ Required
0-RTT connection

Enabling HTTP/2 in Nginx

# Nginx 1.25.1+: use the http2 on directive
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # Enable HTTP/2
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

location / {
proxy_pass http://backend;
proxy_http_version 1.1; # use HTTP/1.1 to the backend (typical)
proxy_set_header Connection "";
proxy_set_header Host $host;
}
}

# Older Nginx (< 1.25.1): append http2 to the listen line
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# ...
}
# Verify HTTP/2 is active
curl -I --http2 https://example.com | grep "HTTP/"
# HTTP/2 200

# Or check the negotiated protocol
openssl s_client -connect example.com:443 -alpn h2 2>/dev/null | grep "ALPN"
# ALPN protocol: h2 ← HTTP/2 negotiated

HTTP/2 Performance Tuning

http {
# Maximum concurrent streams per connection (default: 128)
http2_max_concurrent_streams 256;

# Idle connection timeout
keepalive_timeout 65;
keepalive_requests 1000;
}

Enabling HTTP/2 in Apache (mod_http2)

# Ubuntu
sudo a2enmod http2
sudo systemctl restart apache2

# Verify
apache2ctl -M | grep http2
# http2_module (shared)
# /etc/apache2/conf-available/http2.conf

# Configure globally or per VirtualHost
Protocols h2 h2c http/1.1
# h2 : HTTP/2 over TLS
# h2c : HTTP/2 cleartext (browsers don't use this)
# http/1.1 : fallback

# HTTP/2 requires MPM Event (not Prefork)
# sudo a2dismod mpm_prefork
# sudo a2enmod mpm_event
<VirtualHost *:443>
ServerName example.com

SSLEngine On
SSLCertificateFile /etc/ssl/example.com/fullchain.pem
SSLCertificateKeyFile /etc/ssl/example.com/privkey.pem

Protocols h2 http/1.1
</VirtualHost>

HTTP/3 (QUIC) Configuration

HTTP/3 is experimentally supported from Nginx 1.25.x+. It requires BoringSSL or quictls instead of OpenSSL.

# Check if HTTP/3 module is built in
nginx -V 2>&1 | grep "with-http_v3_module"
server {
# HTTP/3 listener (UDP port 443)
listen 443 quic reuseport;
listen 443 ssl;
listen [::]:443 quic reuseport;
listen [::]:443 ssl;
http2 on;

server_name example.com;

ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
ssl_protocols TLSv1.3; # HTTP/3 requires TLS 1.3

# Alt-Svc header: tells browser HTTP/3 is available
add_header Alt-Svc 'h3=":443"; ma=86400';

location / {
proxy_pass http://backend;
}
}
# Verify HTTP/3 is working
curl -I --http3 https://example.com | grep "HTTP/"
# HTTP/3 200

Client-to-Backend Protocol Strategy

upstream backend {
server app1:8080;
server app2:8080;
keepalive 64;
}

server {
listen 443 ssl;
http2 on;

# Standard HTTP backend
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}

# gRPC backend (HTTP/2)
location /grpc/ {
grpc_pass grpc://grpc_backend:50051;
}
}

HTTP/2 Server Push

HTTP/2 allows the server to proactively send resources without a request. However, its practical value is limited and it was removed in HTTP/3.

location = /index.html {
proxy_pass http://backend;

# Push CSS and JS when HTML is requested (HTTP/2 only)
http2_push /static/app.css;
http2_push /static/app.js;
}

# Or use Link header for dynamic push
location / {
proxy_pass http://backend;
http2_push_preload on;
# Backend returns: Link: </static/app.css>; rel=preload; as=style
}

Practical alternative: <link rel="preload"> hints in HTML are more reliable.


Measuring HTTP/2 Performance

# HTTP/1.1 baseline
ab -n 1000 -c 50 https://example.com/

# HTTP/2 benchmark (h2load)
h2load -n 1000 -c 50 -m 10 https://example.com/
# -m 10 : max 10 concurrent streams per connection

# Install h2load
sudo apt install nghttp2-client

# Typical result comparison:
# HTTP/1.1: ~200 req/s
# HTTP/2: ~800 req/s (multiplexing effect)