HTTP/3 与 QUIC:为什么你的网站该开启它
从一次页面加载说起
打开一个现代网页,浏览器平均要发起 70 到 100 个请求。在 HTTP/2 时代,这些请求共享一条 TCP 连接,理论上很高效——直到其中一个数据包丢了。
TCP 是严格有序的字节流协议:第 5 个包丢了,即使第 6 到 100 个包已经到达,内核也不会把它们交给应用层。整个连接上的所有请求一起等,这就是传输层队头阻塞。
HTTP/2 在应用层做了多路复用,但底层依然是一条 TCP 连接,所以它解决不了这个问题——反而因为把所有请求都塞进一条连接,丢包的影响面更大了。
QUIC 的核心思路
QUIC 的做法是:别改 TCP 了,直接在 UDP 上重新实现一套传输层。
| 特性 | TCP + TLS 1.3 | QUIC |
|---|---|---|
| 传输载体 | 内核 TCP | 用户态 UDP |
| 连接建立 | TCP 三次握手 + TLS 握手 | 合并为 1-RTT |
| 恢复连接 | 重新完整握手 | 0-RTT(有安全权衡) |
| 丢包影响 | 阻塞整条连接 | 只阻塞受影响的流 |
| 连接迁移 | IP 变化即断开 | 靠 Connection ID 保持 |
| 加密范围 | 仅载荷 | 含大部分头部 |
| 升级部署 | 要改操作系统内核 | 改用户态程序即可 |
其中连接迁移这一条经常被低估。手机从 Wi-Fi 切换到 4G 时 IP 地址会变,TCP 连接立刻失效,正在下载的文件要重新开始。QUIC 用与 IP 无关的 Connection ID 标识连接,切换网络后下载可以继续。
队头阻塞到底差多少
假设一条连接上有 6 个并发流,其中一个包丢失,重传需要 1 个 RTT:
HTTP/2 over TCP:
[流1✓][流2✗丢了][流3✓][流4✓][流5✓][流6✓]
└──────── 全部等待重传(1 RTT)────────┘
HTTP/3 over QUIC:
[流1✓][流2✗重传中][流3✓][流4✓][流5✓][流6✓]
└─ 只有流2等待,其余立即可用 ─┘在丢包率 1% 以上的网络里(移动网络、跨国链路),这个差距会非常明显。在理想的有线网络下,两者的差别则几乎可以忽略。
在 Cloudflare 上开启
如果你的域名已经接入 Cloudflare,开启 HTTP/3 只需要点几下:
- 进入域名的 Speed → Settings
- 找到 HTTP/3 (with QUIC),打开开关
- 同时建议开启 0-RTT Connection Resumption
验证是否生效:
# 看 alt-svc 响应头,h3 表示服务器支持 HTTP/3
curl -sI https://example.com | grep -i alt-svc
# 输出形如:
# alt-svc: h3=":443"; ma=86400浏览器端可以在 DevTools 的 Protocol 列里直接看到每个请求用的是 h2 还是 h3。
什么时候不值得开
HTTP/3 不是银弹,以下场景要谨慎:
- 企业内网有 UDP 限制:很多防火墙默认丢弃或限速 UDP 443,用户会静默回退到 HTTP/2
- 需要基于 TCP 的中间设备:老旧的负载均衡、DDoS 清洗设备可能完全不认识 QUIC
- 服务器 CPU 吃紧:QUIC 在用户态处理加解密,比内核 TCP 更耗 CPU
- 需要精确的流量审计:QUIC 头部也被加密,传统基于端口的分析手段会失效
好在浏览器和服务器都支持优雅回退:客户端首次访问走 TCP,服务器通过 alt-svc 头告知"我也支持 h3",下次访问才会尝试 QUIC。所以开启 HTTP/3 几乎不会让现有用户变慢。
实践建议:先在 10% 流量上开启,观察 P95 首字节时间和错误率一周,再全量。
一个容易被忽略的细节:0-RTT 的风险
0-RTT 允许客户端在第一个包里就带上应用数据,看起来很美,但它不具备前向安全性,并且容易受到重放攻击——攻击者可以截获这个包并重复发送。
所以规范上明确要求:0-RTT 只能用于幂等请求。
GET /api/feed ← 可以走 0-RTT
POST /api/transfer ← 绝对不能走 0-RTTCloudflare 默认只在安全的请求上启用 0-RTT,但这个约束在自建服务时经常被忽略。
小结
- HTTP/2 的多路复用受限于 TCP,丢包时全线阻塞
- QUIC 把传输层搬到 UDP 之上去掉了这个限制,代价是用户态开销
- 连接迁移对移动端体验的提升往往比速度提升更有感
- 开启方式极简,且有自动回退,风险很低
- 真正要小心的是 0-RTT 的重放风险,只对幂等请求开放
对绝大多数网站来说,结论很简单:能开就开。