HTTP/3 is already here, and your Nginx server can serve it today without waiting for the "next big thing."
Let’s get Nginx serving HTTP/3 traffic. This means your users will get faster, more reliable connections, especially on less-than-ideal networks, because QUIC (the transport protocol for HTTP/3) handles packet loss and connection establishment much more efficiently than TCP.
Here’s what a minimal Nginx configuration enabling HTTP/3 looks like:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols 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:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
add_header Alt-Svc 'h3=":443"; ma=86400';
root /var/www/example.com;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
The critical pieces here are the listen directives and the add_header Alt-Svc.
The listen 443 quic reuseport; and listen [::]:443 quic reuseport; lines tell Nginx to listen for QUIC connections on UDP port 443. The reuseport option is crucial for performance; it allows multiple Nginx worker processes to bind to the same port, distributing the incoming UDP traffic efficiently. Without it, only one worker would handle all QUIC traffic, creating a bottleneck.
The add_header Alt-Svc 'h3=":443"; ma=86400'; directive is how Nginx advertises its ability to serve HTTP/3. This header, sent in response to HTTP/1.1 or HTTP/2 requests, tells the browser, "Hey, I can also speak HTTP/3 on this same port, and you should try it for the next 86400 seconds (1 day)." This allows for a smooth, zero-downtime transition from existing protocols to HTTP/3.
To make this work, you need a recent version of Nginx compiled with the QUIC and HTTP/3 modules. The easiest way to get this is often through package managers if they provide a "full" or "extras" version, or by building Nginx from source. For example, on Ubuntu, you might install nginx-full or nginx-modules-quic. If you’re building from source, you’ll need to include --with-http_v3_module and --with-stream_quic_module during the ./configure step.
Let’s see this in action. Imagine a user’s browser requests https://example.com/.
- Initial Connection (HTTP/1.1 or HTTP/2): The browser connects to
example.comon TCP port 443. Nginx receives the request and serves the page. - Advertising HTTP/3: Along with the page content, Nginx sends back the
Alt-Svcheader:h3=":443"; ma=86400. - Subsequent Connection (HTTP/3): The browser, seeing the
Alt-Svcheader and supporting HTTP/3, will attempt to establish a QUIC connection toexample.comon UDP port 443 for future requests. This QUIC handshake is significantly faster than TCP’s three-way handshake plus TLS handshake. - HTTP/3 Traffic: All subsequent communication for that session will use QUIC and HTTP/3.
The real power of QUIC lies in its UDP-based nature and its built-in stream multiplexing. Unlike TCP, where a single lost packet can stall all active streams, QUIC’s streams are independent. If a packet is lost on one stream, other streams can continue to make progress. This drastically improves performance on lossy networks, like mobile or Wi-Fi.
When Nginx receives a QUIC connection, it first performs the QUIC handshake. This handshake establishes a secure UDP connection and negotiates encryption parameters. Once established, Nginx can then process HTTP/3 frames over this secure QUIC connection. The reuseport directive ensures that the UDP socket is shared efficiently across Nginx worker processes. Each worker can then handle incoming QUIC packets independently, preventing a single worker from becoming a bottleneck.
A common point of confusion is that HTTP/3 still uses TLS. The ssl_certificate and ssl_certificate_key directives are exactly the same as for HTTP/1.1 and HTTP/2. The TLS handshake is integrated into the QUIC handshake, providing the same level of security but with fewer round trips. You should use modern TLS settings, as shown in the example, prioritizing TLS 1.3 and strong cipher suites.
One subtle aspect of HTTP/3 configuration is understanding the Alt-Svc header’s ma (max-age) parameter. This tells the client how long it should remember that HTTP/3 is available. If you change your HTTP/3 configuration or disable it, you’ll want to decrease this ma value temporarily to ensure clients stop trying to connect via HTTP/3. Once they’ve moved on to HTTP/1.1 or HTTP/2, you can then remove the Alt-Svc header entirely.
After enabling HTTP/3, you’ll likely want to monitor your Nginx error logs for any new issues related to QUIC or UDP socket binding, and you’ll want to verify that clients are indeed using HTTP/3.