HTTP/3 and QUIC Configuration Guide

HTTP/3 uses the QUIC transport protocol over UDP instead of TCP, reducing latency and improving performance on unreliable connections. This guide covers enabling HTTP/3 and QUIC on Nginx and Caddy on Linux, configuring UDP firewall rules, verifying TLS 1.3, and testing client compatibility.

Prerequisites

  • Ubuntu 20.04+/Debian 11+ or CentOS 8+/Rocky Linux 8+
  • A valid SSL/TLS certificate (Let's Encrypt works)
  • Root or sudo access
  • UDP port 443 open in your firewall
  • Nginx 1.25.0+ (for native QUIC support) or Caddy

Understanding HTTP/3 and QUIC

Key differences from HTTP/2:

  • Uses UDP instead of TCP
  • Built-in TLS 1.3 (TLS is part of QUIC, not separate)
  • Eliminates head-of-line blocking at the transport layer
  • Faster connection establishment (0-RTT available)
  • Better performance on lossy connections (mobile networks)

Browsers fall back to HTTP/2 or HTTP/1.1 automatically if QUIC is blocked or unavailable.

Enabling HTTP/3 on Nginx

Nginx 1.25.0+ includes QUIC/HTTP3 support as an experimental feature. Check your version:

nginx -v
# nginx version: nginx/1.25.x

If you have Nginx 1.25+, QUIC may already be compiled in:

nginx -V 2>&1 | grep -o 'with-http_v3_module'
# Should output: with-http_v3_module

Building Nginx with QUIC Support

If your Nginx doesn't include QUIC, build it from the Nginx mainline source:

# Install build dependencies
sudo apt install -y build-essential libssl-dev libpcre3-dev zlib1g-dev \
  libgd-dev libxslt1-dev cmake ninja-build golang-go mercurial

# Download Nginx mainline source
NGINX_VER=1.25.3
wget "https://nginx.org/download/nginx-${NGINX_VER}.tar.gz"
tar xzf "nginx-${NGINX_VER}.tar.gz"

# Get quiche (Cloudflare's QUIC implementation) or use OpenSSL 3.x with QUIC
# Option 1: Use nginx-quic branch (for nginx with BoringSSL)
hg clone https://hg.nginx.org/nginx-quic
cd nginx-quic

# Option 2: Build with OpenSSL 3.x (simpler)
wget https://www.openssl.org/source/openssl-3.1.0.tar.gz
tar xzf openssl-3.1.0.tar.gz

# Build Nginx with HTTP/3
./auto/configure \
  --with-http_v3_module \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-cc-opt="-I../openssl-3.1.0/include" \
  --with-ld-opt="-L../openssl-3.1.0" \
  --with-http_gzip_static_module \
  --prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx \
  --modules-path=/usr/lib64/nginx/modules \
  --conf-path=/etc/nginx/nginx.conf \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx.pid

make -j$(nproc)
sudo make install

Alternatively, use the NGINX official packages which include QUIC in recent mainline builds:

# Add NGINX mainline repo and install
sudo apt install -y nginx-core  # May include QUIC depending on version

Nginx HTTP/3 Configuration

server {
    # HTTP/2 on TCP (standard HTTPS)
    listen 443 ssl;
    http2 on;

    # HTTP/3 on UDP (QUIC)
    listen 443 quic reuseport;

    server_name www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/www.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.yourdomain.com/privkey.pem;

    # TLS 1.3 required for QUIC
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # Enable QUIC-specific TLS settings
    ssl_early_data on;  # 0-RTT support (use with caution - replay attacks)

    # Advertise HTTP/3 support to clients via Alt-Svc header
    add_header Alt-Svc 'h3=":443"; ma=86400';
    add_header QUIC-Status $quic;  # Optional: indicate QUIC usage

    # Standard configuration continues...
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name www.yourdomain.com;
    return 301 https://$host$request_uri;
}

Key points:

  • listen 443 quic reuseport; enables QUIC on UDP
  • add_header Alt-Svc tells browsers to try HTTP/3 next time
  • Both TCP (HTTP/2) and UDP (HTTP/3) listeners are required
sudo nginx -t
sudo systemctl reload nginx

Enabling HTTP/3 on Caddy

Caddy enables HTTP/3 by default in recent versions - no special configuration needed:

# Install Caddy (includes HTTP/3 support)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy

caddy version  # Should be 2.6.0+

Basic Caddyfile with HTTP/3 enabled automatically:

# /etc/caddy/Caddyfile
www.yourdomain.com {
    root * /var/www/html
    file_server

    # HTTP/3 is enabled by default
    # Verify with: curl --http3 https://www.yourdomain.com -I

    # Optional: explicitly configure
    # protocols h3 h2 h1
    
    encode gzip zstd
    
    log {
        output file /var/log/caddy/access.log
    }
}
sudo systemctl reload caddy
# Check status
curl -I https://www.yourdomain.com | grep -i alt-svc

Firewall UDP Configuration

HTTP/3 requires UDP port 443 to be open:

# UFW
sudo ufw allow 443/udp
sudo ufw status

# iptables
sudo iptables -A INPUT -p udp --dport 443 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4

# firewalld (CentOS/Rocky)
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports | grep 443

Verify UDP port 443 is open from outside:

# From a remote machine
nc -u -zv your-server-ip 443

# Or use nmap
nmap -sU -p 443 your-server-ip

TLS 1.3 Requirements

QUIC requires TLS 1.3. Verify your Nginx/Caddy configuration:

# Verify TLS 1.3 is enabled in Nginx
openssl s_client -connect www.yourdomain.com:443 -tls1_3 2>/dev/null | \
  grep -E 'Protocol|Cipher'

# Check OpenSSL version supports TLS 1.3
openssl version  # Need 1.1.1+

# For Ubuntu 20.04+, OpenSSL 1.1.1 is included
# For CentOS 8+, OpenSSL 1.1.1 is included

Required Nginx TLS settings for QUIC:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

Testing HTTP/3

# Install curl with HTTP/3 support
# Ubuntu 22.04+ includes curl with HTTP/3 via ngtcp2
curl --http3 -I https://www.yourdomain.com

# If curl doesn't support HTTP/3, build from source or use Docker
docker run --rm -it ymuski/curl-http3 curl --http3 -I https://www.yourdomain.com

# Use nghttp3 tools
sudo apt install -y nghttp2-client
nghttp -nv https://www.yourdomain.com

# Check using Chromium (shows protocol in DevTools > Network > Protocol column)
# Chrome: chrome://net-internals/#quic

# Online test tools:
# https://www.http3check.net
# https://gf.dev/http3-test

Verify the Alt-Svc header is being sent:

curl -I https://www.yourdomain.com | grep -i alt-svc
# Expected: Alt-Svc: h3=":443"; ma=86400

Troubleshooting

HTTP/3 not working despite correct configuration:

# Check Nginx was compiled with QUIC
nginx -V 2>&1 | grep http_v3

# Check UDP 443 is actually open
sudo ss -ulnp | grep ':443'

# Check for firewall issues (common with cloud providers)
# AWS Security Groups, GCP Firewall rules - add UDP 443 explicitly

Alt-Svc header present but browsers don't upgrade:

Browsers maintain a QUIC blocklist per domain. If a previous connection failed, the domain may be blocked for hours. Clear with:

# Chrome: chrome://net-internals/#quic > Clear QUIC sessions

"ssl_early_data" causing issues:

0-RTT data is subject to replay attacks. Disable if you see unexpected behavior:

ssl_early_data off;

Caddy not showing HTTP/3 in responses:

Check that UDP 443 is open and Caddy is using a version that includes QUIC:

caddy version | grep -v "^$"
# Build info should include QUIC
journalctl -u caddy | grep -i quic

Conclusion

HTTP/3 with QUIC brings measurable latency improvements for users on high-latency or lossy connections, and modern browsers will adopt it automatically once you advertise support via the Alt-Svc header. Caddy provides the simplest path to HTTP/3 with zero extra configuration. For Nginx, ensure you're running version 1.25+ with QUIC compiled in and remember that UDP port 443 must be open in addition to TCP 443. Browser support is now near-universal, making HTTP/3 a safe deployment choice.