Nginx HTTP/3 (QUIC) 启用实战:Alt-Svc、UDP 443 放行、证书链与「开了没生效」四层排查

HTTP/3 到底比 HTTP/2 快在哪

HTTP/3 最大的变化是把传输层从 TCP 换成了 QUIC。QUIC 跑在 UDP 之上,自带了 TLS 1.3,把「建连」和「加密握手」合并在一次往返里完成。传统 HTTPS 要先 TCP 三次握手、再 TLS 握手,至少两个 RTT 才发得出第一个请求;HTTP/3 通常一个 RTT 就够,回访的连接还能 0-RTT 复用。

另一个实打实的收益是彻底解决了 TCP 层的队头阻塞。HTTP/2 虽然做了多路复用,但所有流共享一条 TCP 连接,一旦某个包丢了,整条连接上的所有流都得等重传。QUIC 的多路复用做在 UDP 层,每条流独立,丢一个包只影响那一条流。对移动网络、丢包率高的线路,这个差异非常明显。

对个人站长来说,开启 HTTP/3 的性价比在于:不需要改任何业务代码,只要 Nginx 版本够新、证书没问题,改几行配置就能生效。下面把整个流程讲清楚。

前置条件:Nginx 版本与 QUIC 支持

Nginx 官方的 QUIC 支持从 1.25.0 开始进入 mainline,但真正稳定好用建议 1.25.5 以上。先确认版本:

nginx -v 2>&1
# 需要 >= 1.25.0,且编译时带 --with-http_v3_module

# 看编译参数里有没有 http_v3_module
nginx -V 2>&1 | tr ' ' '\n' | grep v3

如果 nginx -V 输出里没有 --with-http_v3_module,说明这份二进制不支持 HTTP/3。Debian 官方源里的 nginx 通常不带该模块,需要换用 nginx.org 官方源,或者自己编译(编译时要带上 OpenSSL 的 QUIC 分支,比较折腾,这里推荐换源)。

# 加 nginx.org 官方源(以 Debian 12 为例)
curl -fsSL https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
  -o /usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/debian bookworm nginx" \
  > /etc/apt/sources.list.d/nginx.list
apt update && apt install -y nginx

装完再 nginx -V 确认 v3 模块在列。

QUIC 必须依赖 TLS 1.3 和正确的证书

QUIC 内建 TLS 1.3,所以站点必须已经配好了有效的 HTTPS 证书。自签证书在浏览器里会被直接拒绝 HTTP/3 协商,务必用 Let's Encrypt 之类的正式证书,并且 ssl_protocols 里包含 TLSv1.3。

ssl_protocols TLSv1.2 TLSv1.3;
ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

注意 fullchain 必须是完整证书链。HTTP/3 客户端对链不完整比 HTTP/1.1 更敏感,缺中间证书时握手会直接失败。

开启 HTTP/3 的关键配置

在 server 块里,监听 443 的同时多写一条 UDP 的 quic 监听,并加上 Alt-Svc 响应头:

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

    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # 关键:告诉浏览器「我也支持 HTTP/3,下次用 UDP 连我」
    add_header Alt-Svc 'h3=":443"; ma=86400' always;

    # 让 QUIC 的源地址校验更宽松,兼容多网卡/负载均衡场景
    quic_retry on;
}

三点说明:

  • listen 443 quic 必须带 reuseport,否则多 worker 场景下 UDP 包会落到错误的进程。
  • http2 on; 是 1.25.1 之后的新写法,老写法是 listen 443 ssl http2;,两者不要混。
  • Alt-Svc 头是「HTTP/3 能不能被发现」的关键。没有它,浏览器永远只会走 TCP。

放行 UDP 443,这一步最容易漏

HTTP/3 跑在 UDP 上,而绝大多数人的防火墙只放行了 TCP 443。UDP 443 没开,浏览器协商失败后会回落到 HTTP/2——表面看网站正常,实际上 HTTP/3 一次都没生效。

# nftables 放行 UDP 443
nft add rule inet filter input udp dport 443 accept

# 或 iptables
iptables -I INPUT -p udp --dport 443 -j ACCEPT

云服务器还要检查安全组,很多厂商的安全组默认只放 TCP,需要手动加一条 UDP 443 的入站规则。

验证 HTTP/3 是否真的生效

配置 reload 之后,分两步验证。

第一步,看响应头有没有 Alt-Svc:

curl -sI "https://example.com/?_=$(date +%s)" | grep -i alt-svc
# 期望:alt-svc: h3=":443"; ma=86400

第二步,用 curl 强制走 HTTP/3 请求:

# 需要 curl 编译时带 HTTP/3 支持(nginx.org 的 curl 或自编译)
curl --http3 -sI "https://example.com/?_=$(date +%s)" | head -3
# 期望看到 HTTP/3 200

如果 curl --http3 报「the installed libcurl version doesn't support this」,那是 curl 本身不带 QUIC,换一台支持的工具或用浏览器的开发者工具看协议列。在 Chrome 里打开 DevTools → Network,把 Protocol 列调出来,刷新页面,看到 h3 就说明生效。

和 CDN 的关系:别在同一层重复开

如果你的站点前面套了 Cloudflare 之类的 CDN,那么「浏览器到 CDN」这一段由 CDN 提供 HTTP/3,「CDN 到源站」这一段是否用 HTTP/3 由回源配置决定。此时源站单独开 HTTP/3 的意义有限,但仍建议开着,因为某些 CDN 支持用 HTTP/3 回源,且换 CDN 时不用再改配置。

如果 CDN 不支持 HTTP/3 回源,源站开与不开对终端用户没影响,但开了也不会有坏处。真正要注意的是:不要在 CDN 已经终结 TLS 的情况下还去源站强制 HTTP/3,导致回源握手多一层兼容问题。

四个常见「开了没生效」的原因

  1. UDP 443 被防火墙/安全组挡了。 最常见。用 nmap -sU -p 443 example.com 或在线 UDP 探测工具确认端口可达。
  2. 缺少 Alt-Svc 头。 浏览器不会主动去猜你支持 HTTP/3,必须由响应头告知。
  3. 证书链不完整。 HTTP/3 对中间证书更敏感,用 openssl s_client 检查链是否完整。
  4. listen 没带 reuseport。 多 worker 时 UDP 连接被随机分派,导致部分请求失败、部分成功,表现为「时灵时不灵」。

要不要为了 HTTP/3 折腾?

客观地说,HTTP/3 的收益主要体现在:高丢包网络(移动 4G/5G)、大量并发的首屏加载、以及需要频繁建立新连接的场景。对于主要面向国内用户、网络质量稳定的站点,收益没有想象中大,但配置成本也很低,属于「开了不亏」的优化项。

唯一要权衡的是运维复杂度:多了一个 UDP 端口要监控、多了一个 QUIC 相关日志要排查。如果你的监控还没覆盖 UDP 层面,建议先把 HTTP/2、Brotli、缓存这些基础项做扎实,再上 HTTP/3。性能优化永远是这个顺序——先把大盘子做对,再抠最后那几个百分点。

Last modification:October 14th, 2026 at 01:24 pm

Leave a Comment