HTTP/2 sends many requests over one connection in parallel (multiplexing), compresses headers and avoids the per-request connection overhead of HTTP/1.1. HTTP/3 runs HTTP over QUIC, a UDP-based transport with faster connection setup and better behaviour on lossy mobile networks. Both are supported by all modern browsers, and Nginx can serve both.

Requirements

  • HTTP/2 — Nginx 1.9.5+ (any current distribution). Browsers only use HTTP/2 over HTTPS.
  • HTTP/3 — Nginx 1.25.0+ built with QUIC support (--with-http_v3_module) and a TLS library with QUIC support. Ubuntu/Debian’s packaged Nginx may be too old or built without it; the nginx.org “mainline” or “stable” packages include HTTP/3 support in current versions.

Check:

nginx -V 2>&1 | tr ' ' '\n' | grep -E 'http_v2|http_v3'
nginx -v

Enable HTTP/2

Nginx 1.25.1 and newer:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com;
    # ssl_certificate …
}

Older versions:

listen 443 ssl http2;

(The http2 parameter on listen is deprecated in new versions but still accepted.) http2 on applies per server, but because HTTP/2 is negotiated per IP:port, enabling it in one server block on 443 effectively enables it for all virtual hosts on that address.

sudo nginx -t && sudo systemctl reload nginx

Enable HTTP/3

server {
    listen 443 ssl;
    listen 443 quic reuseport;
    listen [::]:443 ssl;
    listen [::]:443 quic reuseport;
    http2 on;
    http3 on;
    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;     # QUIC requires TLS 1.3

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

Notes:

  • reuseport may appear on the quic listen only once per address:port across all server blocks. For additional virtual hosts, use listen 443 quic; without it.
  • Browsers first connect via HTTP/2, see the Alt-Svc header, and switch to HTTP/3 on later requests.

Open UDP 443

QUIC uses UDP. Without a firewall rule, browsers silently fall back to HTTP/2:

sudo ufw allow 443/udp
sudo firewall-cmd --permanent --add-port=443/udp && sudo firewall-cmd --reload

Verify

curl -sI --http2 https://example.com | head -1          # HTTP/2 200
curl -sI https://example.com | grep -i alt-svc
curl -sI --http3-only https://example.com | head -1     # requires curl built with HTTP/3

In Chrome or Firefox developer tools, enable the Protocol column in the Network tab: h2 or h3. Online tools like http3check can test from outside.

Does it make a difference?

  • HTTP/2 is a clear win for pages with many assets — enable it everywhere you have HTTPS. Old HTTP/1.1 tricks (domain sharding, spriting, concatenating everything) are no longer necessary and can even hurt.
  • HTTP/3 helps most on high-latency or lossy connections (mobile, long distances, e.g. visitors far from your server) thanks to faster handshakes and no head-of-line blocking at the transport layer. On good wired connections the difference is smaller.

Things to know

  • HTTP/2 Server Push was removed from browsers and from Nginx — use <link rel="preload"> or 103 Early Hints instead.
  • Behind a CDN (e.g. Cloudflare), the CDN handles HTTP/2 and HTTP/3 to visitors; your origin can use HTTP/1.1 or HTTP/2 to the CDN.
  • Upstream connections from Nginx to your app remain HTTP/1.1 by default — that’s normal and efficient with keepalive.

Related: Let’s Encrypt with Nginx, HTTP/1.1 vs HTTP/2 vs HTTP/3 and QUIC explained.

  • Nginx
  • HTTP/2
  • HTTP/3