Nginx HTTP/3 (QUIC) 实战:自编译、Alt-Svc 通告与 UDP 443 防火墙,附验证方法

HTTP/3 到底值不值得折腾

每次聊到「给网站加速」,绕不开的话题就是 HTTP/3。它基于 QUIC(跑在 UDP 上的传输协议),主打两个卖点:0-RTT 建连和彻底消除队头阻塞。对个人站长而言,最实际的好处是——在弱网、移动网络、跨境访问场景下,首屏加载的感知速度能有可测量的提升。

但 HTTP/3 也是那种「配置细节多、坑不少、效果看场景」的技术。今天这篇不讲玄学,只讲怎么在 Nginx 上真把它跑起来:编译参数、配置块、防火墙、CDN 取舍、以及如何验证握手的的确确走的是 QUIC 而不是 TCP。

前提:Nginx 版本与 QUIC 支持

Nginx 从 1.25.0 开始提供 HTTP/3 的正式实现(早期 1.25.0 之前的那些第三方 patch 就不必用了)。但要提醒几个现实:

  • Debian/Ubuntu 官方仓库里的 Nginx(通常是 1.22/1.24)不带 HTTP/3 模块。
  • 要自己编译,或者用官方 nginx.org 的 nginx-quic 分支/仓库。
  • 编译需要 BoringSSL 或打过补丁的 OpenSSL,这不是一条 apt 命令能搞定的。

所以第一个决策点是:要不要自编译?如果你只是想用上 HTTP/3,而且已经在用 Cloudflare,那答案很明确——让 CDN 扛 HTTP/3,源站继续跑 HTTP/2。Cloudflare 免费套餐就支持 HTTP/3,客户端到 Cloudflare 走 QUIC,Cloudflare 到源站走 HTTP/2,你一行 Nginx 配置都不用改。本文后半部分也会讲这个方案。

如果你确实想自建(比如不用 CDN、或者想彻底掌控),继续往下看。

编译带 QUIC 的 Nginx

准备编译依赖:

apt-get update
apt-get install -y build-essential libpcre3-dev zlib1g-dev libssl-dev git curl

HTTP/3 的 TLS 部分需要 BoringSSL。克隆并构建(步骤较多,官方 nginx-quic 页面有完整脚本,这里给关键步骤):

# 拉取 BoringSSL
git clone --depth 1 https://github.com/google/boringssl.git
cd boringssl
mkdir build && cd build
cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..
ninja
cd ../..

# 拉取 Nginx 源码
git clone --depth 1 -b nginx-quic https://github.com/nginx/nginx.git

配置时把 --with-http_v3_module 打开,并指向 BoringSSL 源码目录:

cd nginx
./auto/configure \
  --with-http_v3_module \
  --with-http_v2_module \
  --with-http_ssl_module \
  --with-cc-opt="-I../boringssl/include" \
  --with-ld-opt="-L../boringssl/build -lstdc++" \
  --prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf

make -j$(nproc)
make install

装完 nginx -V 应该能在编译参数里看到 --with-http_v3_module。看不到就说明没编进去,别急着改配置。

Nginx 配置:quic 监听块

HTTP/3 的监听语法比较特殊,需要同时开 TCP(HTTP/1.1 + HTTP/2)和 UDP(HTTP/3)两个监听,并加上 quic 标志。以下是我实测可用的最小配置:

server {
    listen 443 quic reuseport;
    listen 443 ssl;
    http2 on;

    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    # HTTP/3 必须启用 TLS 1.3
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # 通告客户端本服务支持 HTTP/3
    add_header Alt-Svc 'h3=":443"; ma=86400' always;

    # QUIC 专用参数
    quic_retry on;
    ssl_early_data on;

    location / {
        root /var/www/html;
        index index.html;
    }
}

逐条解释几个关键点:

  • listen 443 quic reuseport;:UDP 监听。HTTP/3 走 UDP,这一行不能少。reuseport 让多 worker 都能接收,提升并发。
  • http2 on;:显式开启 HTTP/2(新语法,旧写法是 listen 443 ssl http2;,两者混用会告警)。
  • add_header Alt-Svc ...:这是 HTTP/3 能不能被发现的关键。浏览器第一次访问走 HTTP/2,看到这个响应头后,才知道「下次可以用 h3 连我」,后续才升级到 QUIC。没有 Alt-Svc,客户端永远不会尝试 HTTP/3。
  • ssl_early_data on;:配合 0-RTT,让回头客首次请求就带数据,省一个往返。但要注意它和后面提到的 proxy_set_header 安全取舍。

防火墙:最容易忘的一步

HTTP/3 跑在 UDP 443 上。你平时开的防火墙规则十有八九只有 TCP 443。没开 UDP 443,浏览器探测 HTTP/3 会超时,然后永远回退到 HTTP/2,你还以为是配置没生效。

# ufw
ufw allow 443/udp
ufw allow 443/tcp

# 或者 iptables
iptables -A INPUT -p udp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

云服务商的安全组(阿里云/腾讯云/AWS)也要单独放行 UDP 443,安全组和系统防火墙是两套,都要配。

验证 HTTP/3 是否真的生效

这一步不做,前面全白搭。三种验证方法:

方法一:curl(需要 curl 支持 HTTP/3 编译)。普通发行版 curl 不支持 --http3,会报 option --http3: is unknown。得用支持 QUIC 的 curl:

curl --http3 -I https://example.com
# 看响应里是否有 alt-svc 头,以及连接是否走 h3

方法二:看 Alt-Svc 响应头。这是最快的一步:

curl -sI https://example.com | grep -i alt-svc
# 期望输出: alt-svc: h3=":443"; ma=86400

方法三:浏览器开发者工具。Chrome/Edge 打开 F12 → Network → 右键表头勾选「Protocol」列。刷新几次页面后,看 Protocol 列是不是显示 h3。第一次访问通常还是 h2(因为要先读到 Alt-Svc),第二次刷新才变 h3。

踩坑提醒:如果你挂了 Cloudflare 且开了代理(橙云),那 Alt-Svc 头是 Cloudflare 加的,你源站 Nginx 的配置对最终用户体验毫无影响——用户连的是 CF 的 HTTP/3。别在源站白折腾。

CDN 方案:更省心的选择

对 90% 的个人站长,我推荐这个方案:

  • 源站 Nginx 保持 HTTP/2 就好,专心做业务。
  • Cloudflare 面板 → Network → 打开 HTTP/3 (with QUIC) 开关。
  • 客户端自动走 QUIC 到 CF 边缘,CF 用 HTTP/2 回源。

好处是零编译、零维护、全球边缘节点就近接入、还白送任播路由优化。唯一的代价是你的 HTTP/3 链路只覆盖「客户端到 CF 边缘」这一段,回源段还是 TCP——但对终端用户的感知速度提升,主要就来自前段。

如果你不想用 Cloudflare,Caddy 也是一个选项:Caddy 原生内置 HTTP/3,配置文件里一行 protocols h1 h2 h3 就搞定,不需要自编译。追求「少折腾」的站长,直接换 Caddy 反而比死磕 Nginx 编译更划算。

几个容易被忽略的细节

  • 0-RTT 重放攻击:ssl_early_data on 让客户端在握手完成前就发数据,对幂等的 GET 没问题,但如果你用它保护 POST 接口,存在被重放的风险。Nginx 会在 $ssl_early_data 变量里标记,建议在后端对非幂等请求做去重校验。
  • UDP 缓冲与丢包:QUIC 对 UDP 丢包敏感。内核 UDP 缓冲区小会导致高并发下丢包。可以调 net.core.rmem_max / wmem_max,并确认 quic_retry on 已开(它能缓解部分放大攻击与连接伪建)。
  • 日志里看不到 h3:Nginx 的 $server_protocol 变量对 HTTP/3 请求会记录为 HTTP/3.0(或 HTTP/3,取决于版本),可以用它确认流量里 h3 的占比。

小结

HTTP/3 不是银弹,但在移动端和跨境访问上是实打实的体验优化。决策路径很简单:能用 CDN 就用 CDN(Cloudflare 一个开关的事),想完全自控再上自编译(BoringSSL + nginx-quic)。无论哪条路,最后都要记住三件事:UDP 443 防火墙要开、Alt-Svc 响应头要发、浏览器 Protocol 列要验证成 h3。

技术选型的本质是性价比。对个人站,把「省下来的运维时间」投入到内容本身,往往比死磕一个协议版本带来的收益更大。HTTP/3 该用,但没必要为它把服务器玩崩。

Last modification:October 5th, 2026 at 07:23 pm

Leave a Comment