Enable HTTP/3 and QUIC: Server Configuration | OpsBlu Docs

Enable HTTP/3 and QUIC: Server Configuration

How to diagnose and fix HTTP/3 and QUIC adoption problems. Covers Alt-Svc header configuration, UDP firewall blocking, fallback to HTTP/2, server...

What HTTP/3 Changes

HTTP/3 replaces TCP with QUIC (a UDP-based transport) for all HTTP communication. The practical differences that matter for website performance:

Feature HTTP/2 (TCP) HTTP/3 (QUIC)
Connection setup 2–3 round trips (TCP + TLS) 1 round trip (0-RTT on reconnect)
Head-of-line blocking One lost packet stalls all streams Only the affected stream stalls
Connection migration Breaks on network change (WiFi→cellular) Survives network changes via connection IDs
Packet loss handling TCP retransmission stalls everything Per-stream retransmission, others continue

When HTTP/3 helps most: High-latency connections (mobile, satellite, international users), lossy networks (WiFi, cellular), and users who switch networks mid-session. On fast, reliable connections (fiber, local CDN), the difference is small.

When HTTP/3 doesn't help: Same-origin requests on reliable networks, pages with few resources, or when the bottleneck is server processing time rather than transport latency.

How HTTP/3 Negotiation Works

HTTP/3 isn't used on the first visit to a site. The browser discovers HTTP/3 support through the Alt-Svc response header:

1. Browser connects via HTTP/2 (TCP) on first visit
2. Server responds with: Alt-Svc: h3=":443"; ma=86400
3. Browser remembers this for 86400 seconds (24 hours)
4. On next request, browser attempts QUIC (UDP port 443)
5. If QUIC fails, browser falls back to HTTP/2 (TCP)

This means: the first page load is always HTTP/2. HTTP/3 only kicks in on subsequent visits — and only if QUIC isn't blocked.

Diagnosing HTTP/3 Issues

Check If Your Server Advertises HTTP/3

# Look for the Alt-Svc header
curl -sI https://example.com | grep -i alt-svc

# Expected output for HTTP/3 support:
# alt-svc: h3=":443"; ma=86400

# If empty, your server isn't advertising HTTP/3

Check If HTTP/3 Actually Works

# Attempt an HTTP/3 connection (requires curl 7.88+ with HTTP/3 support)
curl --http3-only -I https://example.com

# If this fails, HTTP/3 isn't working even though it might be advertised
# Common error: "connection refused" = UDP port 443 blocked

Browser Verification

Chrome: Navigate to your site → DevTools → Network tab → right-click column headers → enable "Protocol" column. Look for h3 (HTTP/3) vs h2 (HTTP/2).

Chrome internal diagnostics:

  • chrome://net-internals/#alt-svc — Shows cached Alt-Svc entries (which sites the browser knows support HTTP/3)
  • chrome://net-internals/#quic — Active QUIC sessions and connection details
  • chrome://flags/#enable-quic — Verify QUIC is enabled (it's on by default)

Firefox: about:networking#http3 — Shows active HTTP/3 connections

Network-Level Check

# Verify UDP port 443 is open (QUIC uses UDP, not TCP)
nc -zuv example.com 443

# Wireshark filter for QUIC traffic
# Filter: quic || udp.port == 443

Common Issues and Fixes

1. Server Not Advertising Alt-Svc

Symptom: Browser always uses HTTP/2 even though you think HTTP/3 is enabled.

Cause: Missing or incorrect Alt-Svc header. The server must explicitly tell browsers that HTTP/3 is available.

nginx (1.25.0+):

server {
    listen 443 ssl;
    listen 443 quic;  # Enable QUIC listener

    http2 on;
    http3 on;         # Enable HTTP/3

    # Tell browsers HTTP/3 is available
    add_header Alt-Svc 'h3=":443"; ma=86400' always;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # Required for QUIC
    ssl_early_data on;
}

Caddy (automatic):

Caddy enables HTTP/3 by default with no configuration needed. It automatically sets the Alt-Svc header. Verify with curl -sI https://example.com | grep alt-svc.

Apache (mod_http2 doesn't support HTTP/3): Use a reverse proxy (Caddy, nginx) in front of Apache, or use a CDN that adds HTTP/3 at the edge.

2. UDP Blocked by Firewall or ISP

Symptom: Alt-Svc header is present but the browser never uses HTTP/3. Protocol column shows h2 on all subsequent visits.

Cause: Corporate firewalls, ISPs, or security appliances block UDP port 443. QUIC uses UDP instead of TCP, and many networks only allow TCP on port 443.

Diagnosis: Check Chrome's chrome://net-internals/#quic — if no QUIC sessions appear for your domain, UDP is likely blocked.

Fix:

  • Server side: Ensure your firewall allows UDP port 443 inbound. For cloud providers:
    • AWS: Security group needs UDP 443 inbound rule (not just TCP)
    • GCP: Firewall rule for UDP 443
    • Azure: NSG rule for UDP 443
  • You can't fix client-side blocking. This is why HTTP/2 fallback must always work. Corporate networks often block UDP 443 — your site must serve content over HTTP/2 (TCP) when QUIC fails.
  • CDNs handle this automatically: Cloudflare, Fastly, AWS CloudFront, and Google Cloud CDN all serve HTTP/3 at the edge with automatic TCP fallback.

3. CDN Not Passing Alt-Svc Header

Symptom: Origin server sends Alt-Svc but it doesn't appear in responses served through the CDN.

Cause: Some CDNs strip the Alt-Svc header from origin responses and add their own (or don't).

Fix — Enable HTTP/3 at the CDN level:

  • Cloudflare: Dashboard → Speed → Optimization → Protocol Optimization → HTTP/3 (toggle on). Cloudflare adds its own Alt-Svc header at the edge.
  • AWS CloudFront: Console → Distribution → Behaviors → Supported HTTP versions → check HTTP/3.
  • Fastly: Dashboard → Origins → HTTP/3. Fastly handles Alt-Svc automatically when enabled.

4. Fallback Not Working (Broken When QUIC Fails)

Symptom: Users on networks that block QUIC experience page load failures or extremely slow loads.

Cause: Client attempts QUIC, waits for timeout (several seconds), then falls back to HTTP/2. In rare cases, broken QUIC implementations cause the fallback to also fail.

Fix: Modern browsers handle fallback automatically — they race QUIC and TCP connections and use whichever succeeds first (Happy Eyeballs v2 for QUIC). If you're seeing fallback issues:

  1. Ensure your server responds correctly on both TCP 443 (HTTP/2) and UDP 443 (QUIC).
  2. Set a reasonable ma (max-age) in Alt-Svc: h3=":443"; ma=86400 (24 hours is standard; use shorter values like 3600 during rollout).
  3. Test by blocking UDP 443 locally and verifying the site loads via HTTP/2.

Measuring HTTP/3 Impact

Protocol Distribution

// What protocol are your real users getting?
const nav = performance.getEntriesByType('navigation')[0];
console.log('Page protocol:', nav.nextHopProtocol);  // 'h3', 'h2', 'http/1.1'

// Protocol distribution across all resources
const protocols = {};
performance.getEntriesByType('resource').forEach(r => {
  const proto = r.nextHopProtocol || 'unknown';
  protocols[proto] = (protocols[proto] || 0) + 1;
});
console.log('Protocol distribution:', protocols);

Connection Time Comparison

// Compare connection setup time (where HTTP/3's 0-RTT shines)
const entries = performance.getEntriesByType('resource');
const byProtocol = { h3: [], h2: [] };

entries.forEach(r => {
  const proto = r.nextHopProtocol;
  const connectTime = r.connectEnd - r.connectStart;
  if (proto && connectTime > 0) {
    (byProtocol[proto] || []).push(connectTime);
  }
});

Object.entries(byProtocol).forEach(([proto, times]) => {
  if (times.length > 0) {
    const avg = times.reduce((a, b) => a + b, 0) / times.length;
    console.log(`${proto}: avg ${Math.round(avg)}ms connect (${times.length} resources)`);
  }
});

Server Configuration Reference

nginx (1.25.0+)

http {
    server {
        listen 443 ssl;
        listen 443 quic reuseport;  # reuseport on first server block only

        http2 on;
        http3 on;
        http3_hq on;  # Optional: HTTP/3 over QUIC (experimental)

        quic_retry on;  # Requires address validation token (mitigates amplification)

        ssl_early_data on;
        ssl_protocols TLSv1.3;  # QUIC requires TLS 1.3

        add_header Alt-Svc 'h3=":443"; ma=86400' always;
    }
}

Caddy

example.com {
    # HTTP/3 is enabled by default in Caddy 2.6+
    # To explicitly control it:
    servers {
        protocols h1 h2 h3
    }
}

LiteSpeed

LiteSpeed Web Server has built-in QUIC/HTTP/3 support. Enable in WebAdmin Console → Listeners → SSL → Enable QUIC.