HTTP/2 与 HTTP/3
http2 指令取代 listen 参数、QUIC 的双 listen 与 Alt-Svc、quic_gso 与 0-RTT 的前置条件。
协议本身的差异见《HTTP/2 与 HTTP/3 协议详解》,本章只讲怎么配。
HTTP/2
语法: http2 on | off;
默认: http2 off;
上下文: http, serverserver {
listen 443 ssl;
http2 on; # ← 新写法
# listen 443 ssl http2; # ← 旧写法,已废弃
}前面提过:listen ... http2 参数已被官方标记为废弃(1.25.1 起分离出独立指令)。
两点差异:
- 旧写法是端口级的:同端口上所有 server 一起开关。
- 新指令上下文是
http, server,可按 server 独立配置。
旧写法目前仍工作,但新配置应该用 http2 on;。
浏览器只在 TLS 上用 HTTP/2(靠 ALPN 协商),所以实际上 http2 on 总是与 listen ... ssl 搭配。明文 h2c 浏览器不支持,官方也已废弃 h2c 升级机制。
常用调优
| 指令 | 作用 |
|---|---|
http2_max_concurrent_streams | 并发流上限,对应 SETTINGS_MAX_CONCURRENT_STREAMS |
http2_recv_buffer_size | 接收缓冲 |
http2_chunk_size | 响应体分块大小 |
http2_max_concurrent_streams 是防 Rapid Reset(CVE-2023-44487)一类流洪泛的关键——nginx 有默认值(128),但高危环境可调低。
http2_push 相关指令已随服务端推送废弃而移除。浏览器已全面移除推送支持,配了也不生效。要优化关键资源加载请改用 103 Early Hints + Link: rel=preload。
HTTP/3 / QUIC
语法: http3 on | off;
默认: http3 on; ← 注意默认是 on
上下文: http, serverhttp3 指令默认就是 on——但它只控制「协商时是否接受 HTTP/3」,真正让 QUIC 生效的是 listen ... quic。
双 listen
官方给的标准配置:
server {
listen 8443 quic reuseport; # UDP,QUIC
listen 8443 ssl; # TCP,HTTP/1.1 + HTTP/2
http2 on;
ssl_certificate ...;
ssl_certificate_key ...;
add_header Alt-Svc 'h3=":8443"; ma=86400';
}四个要点:
一、必须两条 listen。 QUIC 跑在 UDP 上,普通 HTTPS 跑 TCP——两个不同的套接字。同端口号但协议不同,不冲突。
二、官方建议用同一个端口号。 注释原文是 Use same port for HTTP/3 and HTTPS for better compatibility——不同端口会让 Alt-Svc 通告变复杂,且部分网络环境下非 443 的 UDP 更容易被拦。
三、reuseport 写在 quic 那条上,且同 address:port 只能写一次。
四、必须发 Alt-Svc。 这是服务发现的唯一途径——客户端不会凭空知道你支持 h3。
add_header Alt-Svc 'h3=":443"; ma=86400';ma 是通告有效期(秒)。首次访问一定走 TCP,客户端记下通告后下次才尝试 QUIC。想消除这一跳需要配 DNS HTTPS 记录(RFC 9460),那是 DNS 侧的事,nginx 管不了。
别忘了 add_header 的继承覆盖问题——某个 location 里加了别的头,Alt-Svc 就没了,h3 也就不会被发现。
TLS 1.3 是硬前提
QUIC 内建 TLS 1.3,所以:
ssl_protocols TLSv1.2 TLSv1.3; # 默认值已包含 1.3,够用若显式禁掉 TLS 1.3,QUIC 直接不可用。
quic_gso
语法: quic_gso on | off;
默认: quic_gso off;
上下文: http, server启用 UDP 分段卸载(UDP_SEGMENT)批量发包,仅 Linux 且内核支持时有效。
这是针对 HTTP/3 的 CPU 开销问题的优化——UDP 路径的每包开销远高于 TCP,GSO 能把多个包合并成一次系统调用。高流量场景值得开,但要实测:内核/网卡不支持时可能反而出问题。
quic_retry
语法: quic_retry on | off;
默认: quic_retry off;
上下文: http, server启用 QUIC 地址验证(Retry 包 + token)。
作用是防 放大攻击——攻击者伪造源 IP 发小包,让服务器向受害者发大量数据。开启后服务器会先要求客户端证明自己确实在那个地址上,代价是多一个 RTT。
公网暴露的服务建议开启。
0-RTT 的前置条件
官方特别注明:
0-RTT support requires OpenSSL 3.5.1+ or BoringSSL/LibreSSL/QuicTLS. Before NGINX 1.29.1, 0-RTT could not be enabled with OpenSSL regardless of the
ssl_early_datadirective value.
历史上用 OpenSSL 编译的 nginx,ssl_early_data on; 对 QUIC 完全无效——不报错,就是不生效。这个坑很隐蔽:配置看着对,抓包发现从来没有 0-RTT。
条件:nginx ≥ 1.29.1 且 OpenSSL ≥ 3.5.1(或用 BoringSSL/QuicTLS 编译)。
nginx -V 可以看编译时链接的 TLS 库。
即便可用,也请先读 0-RTT 的重放风险——默认关闭是有道理的。
http3_hq
语法: http3_hq on | off;
默认: http3_hq off;HTTP/0.9-over-QUIC,仅用于 QUIC 互操作性测试。生产环境不要碰。
完整配置
server {
# TCP:HTTP/1.1 + HTTP/2
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
# UDP:HTTP/3
listen 443 quic reuseport;
listen [::]:443 quic;
http3 on;
server_name example.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
quic_retry on;
# quic_gso on; # Linux 上实测后再开
# 必须:告诉客户端有 h3
add_header Alt-Svc 'h3=":443"; ma=86400' always;
}防火墙记得放行 UDP 443——这是 h3 上不去最常见的原因,且现象是「静默回退到 TCP」,不报任何错。
验证
curl -sI --http3 https://example.com | head -1curl -sI --http2 https://example.com | head -1浏览器 DevTools 的 Network 面板加一列 Protocol,能看到 h3 / h2 / http/1.1。
必须保留 HTTP/2 与 HTTP/1.1 回退。 部分企业网络与运营商限制 UDP,只开 h3 会让这些用户完全无法访问——详见迁移注意事项。上面的双 listen 配置天然满足这一点。
下一篇:缓存。