安装 nginx-quic,启用 HTTP/3

本文最后更新于:2022年3月19日 早上

计算机网络技术日新月异,智能手机已成为人类生活必需品,全球网络用户爆发式增长,人们对网络的需求越发的强烈。老旧的互联网协议无法满足现阶段人们的需求,特别是超文本传输协议(HyperText Transfer Protocol,缩写:HTTP)这种用户使用最多应用层协议,其发展改变了人们浏览网页、观看视频、收听音乐的方式。

在 2018 年 10 月 28 日的邮件列表讨论中,互联网工程任务组(Internet Engineering Task Force,缩写 IETF)HTTPQUIC 工作组主席 Mark Nottingham 提出了将 HTTP-over-QUIC 更名为 HTTP/3 的正式请求,这是 Web 的新标准,可实现更快、更可靠、更安全的 Web 终端(如网站和 API)连接[1]

Nginx 是一个非常优秀的开源网页服务器(Web Server),2020 年 F5, Inc 创建了 nginx-quic 分支来支持 QUIC+HTTP/3[2]。我们可以在 Mercurial 代码库中下载最新的 nginx-quic 源码,通过编译安装 nginx-quic 来给网站提供 HTTP/3 的支持。

一、什么是 HTTP/3

HTTP/3 是自 2015 年 HTTP/2 标准获得批准以来对超文本传输协议的首次重大升级。与其前任 HTTP/1.1 和 HTTP/2 不同,HTTP/3 将弃用 TCP 协议,改用基于 UDP 的 QUIC 协议。此变化主要为了解决 HTTP/2 中存在的队头阻塞问题。由于 HTTP/2 在单个 TCP 连接上使用了多路复用,受到 TCP 拥塞控制的影响,少量的丢包就可能导致整个 TCP 连接上的所有流被阻塞[3]

对HTTP传输协议堆栈的概述
图1 对HTTP传输协议堆栈的概述[4]

基于 UDP 的 QUIC 协议能够改善 HTTP/2 的显著缺点[5],包括:

  • 解决智能手机从 WiFi 切换到蜂窝数据时(例如离开家或办公室时)网络连接卡顿或者重连的问题
  • 减少数据包丢失的影响。当一个数据包无法到达目的地时,它将不再阻塞所有信息流(这个问题称为”队头阻塞”)

其优势包括:

  • 更快地建立连接:QUIC 允许 TLS 版本协商与加密以及传输握手同时进行
  • 零往返时间(0-RTT):对于已经连接到的服务器,客户端可以跳过握手要求(相互确认和验证以确定它们将如何通信的过程)
  • 更全面的加密:QUIC 的新握手方法将默认提供加密。这是对 HTTP/2 的一次重要升级,有助于降低攻击风险。

目前,服务端上,Nginx、Windows Server 2022 和 Windows 11 中的 IIS 均已支持 QUIC,Cloudflare 也在其 CDN 服务中开启 QUIC。客户端上,Google Chrome 和 Microsoft Edge 87 及以上版本、Firefox 88 及以上版本都已默认开启了对 HTTP/3 的支持。macOS Big Sur 11 和 iOS 14 中的 Safari 开始实验性的支持 HTTP/3,但是默认没有启用。

Windows Server 2022和Windows 11的IIS默认启用QUIC
图2 Windows Server 2022和Windows 11的IIS默认启用QUIC

这篇文章主要介绍如何在 ubuntu 20.02 LTS 上安装 nginx-quic 来搭建 Web 服务器,并启用网站对 HTTP/3 的支持。

二、编译安装 nginx-quic

我们在 Mercurial 代码库中下载最新的 nginx-quic 源码,进行编译安装。

1. 搭建编译环境

编译程序需要先搭建编译环境,安装依赖库以及编译所需要的工具。在终端输入以下指令:

1
2
3
sudo apt update
sudo apt install -y build-essential libtool git mercurial # 编译工具
sudo apt install -y libpcre2-dev zlib1g-dev # 依赖库
详细说明
  • build-essentiallibtool 是编译所必需的软件,包括 gccg++make等。必需!
  • gitmercurial 用于从 Github 和 Mercurial 上下载源码。必需!
  • libpcre2-dev 是 nginx 支持 perl 兼容的正则表达式的依赖库。必需!
  • zlib1g-dev 是 nginx 启用 gzip 压缩的依赖库。推荐!

2. 下载 nginx-quic 源码文件

1
2
3
4
5
6
7
8
9
# https://hg.nginx.org/nginx-quic/file/tip/src/core/nginx.h
# 自定义 nginx 最新版本
NGINX_VERSION=1.21.7

# https://hg.nginx.org/nginx-quic/shortlog/quic
# 自定义 nginx-quic 最新提交版本
NGINX_COMMIT=ce6d9cf0f567

hg clone -b quic --rev $NGINX_COMMIT https://hg.nginx.org/nginx-quic /usr/src/nginx-$NGINX_VERSION

定义 NGINX_VERSIONNGINX_COMMIT 变量,主要是为了方便大家在 hg.nginx.org 上检索最新的 nginx-quic 提交版本。由于 nginx-quic 仍属于实验性版本,请确保你所安装的 nginx-quic 永远是最新的。

3. 下载和编译 quictls

QuicTLS 是 OpenSSL 的一个分支,其中添加了可供 QUIC 实现 TLS 握手的 API,用于启用 nginx 的 http_v3_module

1
2
3
4
5
6
7
8
9
10
# https://github.com/quictls/openssl
# 自定义 quictls 最新提交版本
QUICTLS_COMMIT=ab8b87bdb436b11bf2a10a2a57a897722224f828

cd /usr/src && \
git clone https://github.com/quictls/openssl && \
cd openssl && \
git checkout $QUICTLS_COMMIT && \
./Configure --prefix=/usr/src/quictls --openssldir=/usr/src/quictls && \
make install_dev

4. 下载 nginx brotli 压缩模块(推荐)

Brotli 是一款通用的无损压缩算法,它使用 LZ77 算法的一种现代变体(a modern variant of the LZ77 algorithm)、霍夫曼编码(Huffman coding)和二阶上下文建模(2nd order context modeling)的组合来压缩数据,其压缩率可与当前最佳的通用压缩方法相媲美。它在压缩速度上与 deflate 相似,但提供更高的压缩率,开启 Brotli 压缩可以优化网站访问速度[6]

我们需要从 Github 下载 ngx_brotli 源码,并在编译 nginx 时加入这个模块:

1
2
3
4
5
6
7
8
9
10
11
# https://github.com/google/ngx_brotli
# 自定义 ngx_brotli 最新提交版本
NGX_BROTLI_COMMIT=9aec15e2aa6feea2113119ba06460af70ab3ea62

mkdir /usr/src/ngx_brotli && \
cd /usr/src/ngx_brotli && \
git init && \
git remote add origin https://github.com/google/ngx_brotli.git && \
git fetch --depth 1 origin $NGX_BROTLI_COMMIT && \
git checkout --recurse-submodules -q FETCH_HEAD && \
git submodule update --init --depth 1

5. 下载 ngx_security_headers 模块 (推荐)

这个模块可以给 nginx 自动添加安全响应头或者移除一些不安全的响应头,非常推荐编译进 nginx 中。

1
2
3
4
5
6
7
# https://github.com/GetPageSpeed/ngx_security_headers
# 自定义 ngx_security_headers 最新提交版本
NGX_SECURITY_HEADERS_COMMIT=af3d4e79771edebfdcde5472acec50ee52379cff

git clone https://github.com/GetPageSpeed/ngx_security_headers /usr/src/ngx_security_headers && \
cd /usr/src/ngx_security_headers && \
git checkout $NGX_SECURITY_HEADERS_COMMIT

6. 编译安装 nginx

和 Windows 系统中安装程序类似,我们需要自定义程序的安装路径,并设置程序所需启用的功能和模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
CONFIG="\
--build=quic-$NGINX_COMMIT-quictls-$QUICTLS_COMMIT \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=www-data \
--group=www-data \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-http_gunzip_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_slice_module \
--with-stream \
--with-stream_quic_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-stream_realip_module \
--with-compat \
--with-threads \
--with-file-aio \
--with-http_v2_module \
--with-http_v3_module \
--add-module=/usr/src/ngx_brotli \
--add-module=/usr/src/ngx_security_headers \
"

定义 $CONFIG 变量是为了方便选择想启用的功能[7]

详细说明
  • --with-http_ssl_module 为 HTTPS 提供必要的支持。必需!
  • --with-http_realip_module 用于透过代理(反代服务器或 CDN)获取客户端的真实 IP 地址。推荐!
  • --with-http_gzip_static_module 允许发送以 .gz 结尾的预压缩文件替代普通文件。推荐!
  • --with-http_gunzip_module 是一个过滤器,用于对不支持 gzip 编码方法的客户端解压缩。需要存储压缩数据以节省空间并降低 I/O 成本时,且配置了 gzip_static always; 时,该模块将非常有用。推荐!
  • --with-http_secure_link_module 用于开启防盗链功能,检查请求链接的真实性,保护资源免受未经授权的访问,并限制链接有效时长。可选
  • --with-http_stub_status_module 提供对基本状态信息的访问的支持。可选
  • --with-http_auth_request_module 基于子请求结果实现客户端授权。如果子请求返回一个 2xx 响应代码,则允许访问。如果返回 401 或 403,则拒绝访问并抛出相应的错误代码。子请求返回的任何其他响应代码被认为是一个错误。可选
  • --with-http_slice_module 模块用在 proxy_cache 大文件的场景,将大文件切片缓存。通常用在 CDN 的 Range 缓存上。可选
  • --with-stream 等模块用来实现四层协议的转发、代理或者负载均衡。可选
  • --with-threads--with-file-aio 启用线程池和异步 I/O,可以加快 nginx 运行速度。推荐!
  • --with-compat 可以让 nginx 在不重新编译的情况下动态加载模块。推荐!
  • --with-http_v2_module--with-http_v3_module 用于开启 HTTP/2 和 HTTP/3 的支持。必需!
  • --add-module=/usr/src/ngx_brotli 将 brotli 压缩模块编译到 nginx,使 nginx 支持 brotli 压缩。推荐!
  • --add-module=/usr/src/ngx_security_headers 将 ngx_security_headers 模块编译到 nginx。推荐!

Nginx 编译时不是功能加的越多越好,应该尽可能少编译模块,不用的最好不要加入,直接在 $CONFIG 中删掉对应的行即可。定义好 $CONFIG 变量后进行配置:

1
2
3
4
cd /usr/src/nginx-$NGINX_VERSION && \
./auto/configure $CONFIG \
--with-cc-opt="-I /usr/src/quictls/include" \
--with-ld-opt="-L /usr/src/quictls/lib64"

配置结束后,注意查看屏幕上显示的结果,被添加的模块其依赖库应该都被找到,如下面所示:

1
2
3
4
5
6
7
8
9
configuring additional modules
adding module in /usr/src/ngx_brotli
+ ngx_brotli was configured
adding module in /usr/src/ngx_security_headers
+ ngx_http_security_headers_module was configured
checking for PCRE2 library ... found
checking for OpenSSL library ... found
checking for OpenSSL QUIC support ... found
checking for zlib library ... found

特别注意屏幕上 OpenSSL QUIC support 是否显示为 foundOpenSSL QUIC support 是启用 HTTP/3 的关键,如果是 not found,则需要检查上一步 configure 时填写的 quictls 的 includelib64 目录路径是否正确。

配置无误后,便可以进行编译安装了,终端中输入以下指令:

1
2
3
4
5
6
7
make && \
make install && \
strip /usr/sbin/nginx* && \
strip /usr/lib/nginx/modules/*.so && \
ln -s /usr/src/quictls/lib64/libcrypto.so.81.3 /usr/lib/libcrypto.so.81.3 && \
ln -s /usr/src/quictls/lib64/libssl.so.81.3 /usr/lib/libssl.so.81.3 && \
mkdir /var/cache/nginx

7. 检查 nginx

至此,nginx 就已经安装好了,你可以通过在终端中输入 nginx -V,查看 nginx 的版本和编译选项:

1
2
3
4
5
nginx version: nginx/1.21.7 (quic-ce6d9cf0f567-quictls-ab8b87bdb436b11bf2a10a2a57a897722224f828)
built by gcc 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04)
built with OpenSSL 3.0.1+quic 14 Dec 2021
TLS SNI support enabled
configure arguments: --build=quic-ce6d9cf0f567-quictls-ab8b87bdb436b11bf2a10a2a57a897722224f828 --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=www-data --group=www-data --with-http_ssl_module --with-http_realip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_stub_status_module --with-threads --with-stream --with-stream_quic_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-http_slice_module --with-file-aio --with-http_v2_module --with-http_v3_module --add-module=/usr/src/ngx_brotli --add-module=/usr/src/ngx_security_headers --with-cc-opt='-I /usr/src/quictls/include' --with-ld-opt='-L /usr/src/quictls/lib64'

8. 添加 nginx.service 使其开机自启

(1) 添加 nginx 服务

首先在 /usr/lib/systemd/system 目录下新建 nginx.service,在终端中输入以下指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat >>/usr/lib/systemd/system/nginx.service <<'EOF'
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

(2) 重新加载 systemd

1
systemctl daemon-reload

(3) 设置 nginx 开机自启动

1
systemctl enable nginx.service 

(4) nginx 服务常用操作

1
2
3
4
5
6
7
8
9
10
#查看nginx服务状态
systemctl status nginx.service
#启动nginx服务
systemctl start nginx.service
#停止nginx服务
systemctl stop nginx.service
#重启nginx服务
systemctl restart nginx.service
#重新读取nginx配置(这个最常用, 不用停止nginx服务就能使修改的配置生效)
systemctl reload nginx.service

三、配置 nginx.conf 启用 HTTP/3

HTTP/3 使用 QUIC 协议,并且使用 TLS 1.3 进行加密。若要开启网页对 HTTP/3 的支持,必须开启 QUIC 端口(为了更好的兼容性,通常将 quic 和 https 设置为相同的端口),并使用 TLS 1.3 协议加密。你需要在 /etc/nginx/nginx.conf 中进行设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http {
server {
listen 443 ssl http2;
listen 443 http3 reuseport;

server_name example.com;

ssl_certificate /path/to/example.com.crt;
ssl_certificate_key /path/to/example.com.key;
ssl_protocols TLSv1.3;

location / {
root html;
index index.html index.htm;

add_header Alt-Svc 'h3=":443"; ma=86400';
add_header QUIC-Status $http3;
}
}
}

在正确设置 nginx.conf 后,网站依然没有启用 HTTP/3,极有可能是你没有在防火墙中打开 QUIC 对应的 UDP 端口,因为 QUIC 协议基于 UDP 协议进行传输。

防火墙中开启UDP端口
图3 防火墙中开启UDP端口

若需开启 0-RTT,可以在 server 块或 location 块中添加 ssl_early_data on;

四、测试网站是否支持 HTTP/3

测试网站是否支持 HTTP/3 的最简单方法就是在 http3check.net 上输入域名进行测试。

http3check.net测试结果
图4 http3check.net测试结果

当然也可以通过浏览器的开发人员工具来查看是否开启 HTTP/3 访问。

浏览器开发人员工具中查看网站是否开启http3
图5 浏览器开发人员工具中查看网站是否开启http3

同样也可以在手机上查看,是否使用 QUIC 连接访问网站。

手机浏览器中查看是否使用QUIC进行连接
图6 手机浏览器中查看是否使用QUIC进行连接

由于最新版本的 nginx-quic 弃用了 QUIC 的互联网候选版本(Internet drafts),仅支持 IETF QUIC 第一版。所以目前支持最新版本的 nginx-quic 的客户端只有 Firefox 90+ 和 Chrome 92+ (QUIC version 1)[8]

五、总结

HTTP/3 是未来趋势,越来越多的网站开始启用 HTTP/3,它的潜力将在不久的未来被开发到极致。我们现在就可以开始部署,让你的网站有更完美的浏览体验。


参考资料

  1. Nottingham, Mark. Identifying our deliverables. IETF Mail Archive. October 28, 2018
  2. Liam Crilly. Our Roadmap for QUIC and HTTP/3 Support in NGINX. July 12, 2021
  3. HTTP/3 - 维基百科,自由的百科全书. (https://zh.wikipedia.org/wiki/多重要素驗證)
  4. Liam Crilly. Introducing a Technology Preview of NGINX Support for QUIC and HTTP/3. June 10, 2020
  5. What is HTTP/3? (https://www.cloudflare.com/zh-cn/learning/performance/what-is-http3/)
  6. Github - google/ngx_brotli: NGINX module for Brotli compression. (https://github.com/google/ngx_brotli)
  7. NGINX中文文档. (https://docshome.gitbook.io/nginx-docs/he-xin-gong-neng/http)
  8. README: Experimental QUIC support for nginx. (https://quic.nginx.org/readme.html)

安装 nginx-quic,启用 HTTP/3
https://vickey.fun/2022/03/19/install-nginx-quic-to-support-HTTP3/
作者
饶玮琪
发布于
2022年3月19日
许可协议