In comparison to HTTP/2 and earlier versions of HTTP, HTTP/3, based on the QUIC (Quick UDP Internet Connections) protocol, achieves lower latency, improved network adaptability, and enhanced security. Enabling HTTP/3 support in Nginx can enhance performance and user experience, with the following key benefits:

  1. Lower Latency: QUIC’s 0-RTT and 1-RTT handshake mechanisms reduce connection establishment and retransmission times, accelerating page loads.
  2. Multiplexing: HTTP/3 allows multiple requests over a single connection via UDP, offering better recovery and reduced blocking compared to TCP.
  3. Fast Recovery: QUIC uses custom congestion control algorithms to quickly recover from packet loss, improving transmission efficiency.
  4. Enhanced Security: As an application-layer encryption protocol, QUIC provides end-to-end encryption, safeguarding data from man-in-the-middle attacks.
  5. Improved Network Adaptability: QUIC enables changing IP addresses or ports without disrupting sessions, enhancing support for mobile devices switching between networks.

To enable QUIC support in Nginx, OpenSSL or BoringSSL must be integrated during compilation. Manual compilation is complex, using a precompiled version is more convenient.

Below is an example demonstrating the use of the official precompiled version of Nginx on Rocky Linux 9.4:

Install Nginx (Rocky Linux 9)

To add the repo, create a file named /etc/yum.repos.d/nginx.repo with the following contents:

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
# update repo
dnf update
# install nginx
dnf install nginx

Modify the configuration file to enable HTTP/3

Edit /etc/nginx/nginx.conf

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    server_tokens          off;
    charset                utf-8;
    sendfile               on;
    tcp_nopush             on;
    tcp_nodelay            on;
    log_not_found          off;
    types_hash_max_size    2048;
    types_hash_bucket_size 64;
    client_max_body_size   16M;
    keepalive_timeout  65;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    # gzip
    gzip              on;
    gzip_min_length   1k;
    gzip_vary         on;
    gzip_proxied      any;
    gzip_comp_level   5;
    gzip_types        text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

    # SSL
    ssl_session_timeout    1d;
    ssl_session_cache      shared:SSL:10m;
    ssl_session_tickets    off;

    # Diffie-Hellman parameter for DHE ciphersuites
    # Generate the dhparam.pem file for forward secrecy
    # openssl dhparam -out /etc/nginx/dhparam.pem 2048
    ssl_dhparam            /etc/nginx/dhparam.pem;

    # Mozilla Intermediate configuration
    ssl_protocols         TLSv1.3 TLSv1.2;
    ssl_ciphers            HIGH:!aNULL:!MD5;

    # OCSP Stapling
    ssl_stapling           off;
    ssl_stapling_verify    off;
    # DNS
    resolver               1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=60s;
    resolver_timeout       2s;

    include /etc/nginx/http.d/*.conf;
}

Edit /etc/nginx/http.d/example.com.conf

server {
    listen [::]:443 quic;
    listen 443 quic;
    listen [::]:443 ssl;
    listen 443 ssl;
    listen [::]:80;
    listen 80;

    http2 on;

    # enable http3
    http3 on;
    add_header Alt-Svc 'h3=":443"; ma=86400';
    # enable 0-RTT
    ssl_early_data on;

    # openssl rand 80 > /srv/ssl/ssl_session_ticket_key.key
    ssl_session_ticket_key /srv/ssl/ssl_session_ticket_key.key;

    server_name www.example.com example.com;

    # redirect non-www to www, http to https
    if ($http_host = "example.com") {
      return 301 https://www.example.com$request_uri;
    }
    if ($scheme = "http" ) {
      return 301 https://www.example.com$request_uri;
    }

    # root
    root   /srv/web/example.com/www_public;
    index  index.html;
    #try_files $uri $uri/ /index.html;

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # SSL
    ssl_certificate     /srv/ssl/example.com_fullchain.cer;
    ssl_certificate_key /srv/ssl/example.com.key;

    # logging
    access_log          /var/log/nginx/example.com.access.log combined buffer=512k flush=1m;
    error_log           /var/log/nginx/example.com.error.log warn;
}

Key configuration for HTTP/3

# use port 443 for quic
listen [::]:443 quic;
listen 443 quic;

# enable http/3
http3 on;

# required for browsers to direct them to quic port; expired in 24h
add_header Alt-Svc 'h3=":443"; ma=86400';

# enable 0-RTT
ssl_early_data on;

Start Nginx

# test the config file
nginx -t
# start nginx automatically when system boots
systemctl enable nginx
# start nginx
systemctl start nginx

Precompiled Packages and Reference