HTTP/3 and NGINX QUIC project
HTTP/3 is the third iteration of the protocol empowering the World Wide Web.
It uses QUIC transport, which works over UDP. UDP has a better network latency compared to TCP and is supported by the majority of browsers.
NGINX, the web server #1, now officially supports HTTP/3 and its QUIC transport.
NGINX QUIC packages by GetPageSpeed
With the GetPageSpeed repository, you can quickly install NGINX with full QUIC protocol support, and enable HTTP/3 for your websites. GetPageSpeed NGINX QUIC packages are based on QuicTLS which is a special OpenSSL version maintained by joined effort of Akamai and Microsoft. QuicTLS is a better option compared to BoringSSL because it supports OSCP stapling, just like regular OpenSSL.
Installation is free for Fedora Linux, however, requires a subscription for RHEL-based operating systems like CentOS, Rocky Linux, and Amazon Linux.
Supported operating systems:
- Amazon Linux 2
- CentOS/RHEL 7
- CentOS/RHEL 8 and any clones like Rocky Linux and AlmaLinux
- CentOS/RHEL 9 and any clones like Rocky Linux and AlmaLinux
- Fedora Linux, the last two releases
No matter which of the supported operating system you use, installation involves the following:
- Install the GetPageSpeed release package (and subscribe, unless you use Fedora Linux)
- Install the
nginx
package
Install NGINX QUIC in CentOS/RHEL 7, and Amazon Linux 2
sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm
sudo yum -y install epel-release
sudo yum -y install nginx
If you want to install any of the NGINX Extras module packages like PageSpeed or Brotli, you can do so by installing individual module packages:
sudo yum -y install nginx-module-brotli
Don’t forget to follow the instructions of enabling and configuring the respective module, once installed.
Install NGINX with full QUIC support in CentOS/RHEL/Rocky Linux 8, 9, or Fedora Linux
sudo dnf -y install https://extras.getpagespeed.com/release-latest.rpm dnf-plugins-core
sudo dnf -y install nginx
Likewise, if you want to install any of the NGINX Extras module packages like PageSpeed or Brotli:
sudo dnf -y install nginx-module-brotli
Don’t forget to follow the instructions of enabling and configuring the respective module, once installed.
Enable HTTP/3 for your websites
Some headers must be explicitly set for HTTP/3 support:
Alt-Svc: h3=":443"; ma=2592000; persist=1
advertises that HTTP/3 is available on the given port443
, instructs browsers to remember this for 30 days, and persist this information when client’s network configuration changesQUIC-Status: $http3
as a troubleshooting header. When QUIC has been successfully configured, this header will appear as a response header such asQUIC-Status: h3
orQUIC-Status: hq
.
It is best to use the more_set_headers
directive that comes with the headers-more module for setting the headers above.
To install it, run yum -y install nginx-module-headers-more
then add the following at the top of nginx.conf
:
load_module modules/ngx_http_headers_more_filter_module.so;
The configuration of HTTP/3 (QUIC) for a website is pretty straightforward.
You need to add a new listen
directive for NGINX to listen on the UDP port.
server {
listen 443 ssl; # TCP listener for HTTP/1.1
listen 443 quic reuseport; # UDP listener for QUIC+HTTP/3
ssl_protocols TLSv1.3; # QUIC requires TLS 1.3
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
http2 on;
http3 on;
more_set_headers 'Alt-Svc: h3=":443"; ma=2592000; persist=1';
more_set_headers 'QUIC-Status: $http3';
}
Note that the reuseport
flag can be specified only once per listening port. So we recommend to set it only for a single server
that you designate as the default_server
, e.g.:
server {
# ...
listen 443 ssl default_server;
listen 443 quic reuseport default_server;
server_name example.com;
# ...
}
server {
# ...
listen 443 ssl;
listen 443 quic;
server_name example.org;
# ...
}
Also, if you use IPv6 for your domain, be sure to throw in a couple more directives for NGINX to listen on IPv6 for the same:
listen [::]:443 ssl; # IPv6 TCP listener for HTTP/1.1
listen [::]:443 quic reuseport; # IPv6 UDP listener for QUIC+HTTP/3
Likewise, remember that reuseport
can be specified once per port, so put it only under a single server
that is flagged with default_server
marker.
Adjust PHP-FPM link from NGINX to emulate the Host
header
NGINX passes some parameters to PHP-FPM, and many websites rely on the Host
HTTP header for various function.
In HTTP/2, the :authority
pseudo-header field replaces the Host
header, and NGINX does the job of emulating the Host
header, which is available as $_SERVER['HTTP_HOST']
in PHP code.
However, in HTTP/3 implementation, such emulation does not exist. The PHP code will not see HTTP_HOST
as set and will emit errors to logs:
PHP Warning: Undefined array key “HTTP_HOST”
The current solution for PHP websites which work with HTTP/3-enabled NGINX is to add such emulation explicitly via configuration:
location \.php$ {
include fastcgi_params;
fastcgi_param HTTP_HOST $host;
fastcgi_pass …;
}
Distribution-specific options
Some of QUIC options can be enabled to further enhance performance of the protocol.
However, whether they are supported, depends on the kernel installed, and thus, the distribution in use.
If you haven’t upgraded your kernel explicitly and use the default for your distribution, here’s an overview of the options you can enabled depending on distribution.
RockyLinux 9 and higher
You can enable quic_bpf on;
This enables routing of QUIC packets, thus supporting QUIC connection migration.
You can also enable quic_gso on;
which enables sending in optimized batch mode using segmentation offloading.
SELinux notes
Since NGINX now listens on a privileged port which is not part of the default HTTP context, NGINX would fail to start:
root user “nginx: [emerg] bind() to 0.0.0.0:443 failed (13: Permission denied)”
You must add the UDP port 443 to the http_port_t
context:
semanage port -a -t http_port_t -p udp 443
Adjust FirewallD
Usually, FirewallD comes with pre-defined service definitions, and HTTPS is one of the services provided.
However, the service definition currently supports TCP protocol only. With HTTP/3 you must explicitly allow UDP connection over the TLS port 443.
Here’s how to do it:
# for UDP connectivity:
firewall-cmd --permanent --add-port=443/udp
# for TCP connectivity:
firewall-cmd --permanent --add-service=https
firewall-cmd --reload