HTTP/3 与 QUIC 协议
HTTP/3 把传输层从 TCP 换成基于 UDP 的 QUIC。它解决的是 TCP 的老问题:队头阻塞和握手慢。
一、TCP 层的队头阻塞
HTTP/2 用多路复用解决了应用层的队头阻塞:一个 TCP 连接上可以并发多个流,不用再为每个请求排队。
但它仍然跑在 TCP 上,而 TCP 的队头阻塞在传输层:TCP 只认字节流,不认「流」的概念。一个包丢了,后续所有数据都要等它重传——即使这些数据属于完全不同的请求。结果是:丢包率越高,HTTP/2 的多路复用反而可能比多个 HTTP/1.1 连接更慢,因为所有流一起卡住。
这是 HTTP/2 解决了一半、剩下那一半的问题,也是 HTTP/3 存在的理由。
二、把传输层搬到用户态
QUIC 在 UDP 之上自己实现了可靠传输,带来四个关键变化:
① 流之间独立。 QUIC 原生有多条流,一条流丢包只阻塞那条流,其余照常传输。这从根本上消除了传输层的队头阻塞。
② 建连更快。 QUIC 把传输握手与 TLS 握手合并,首次连接 1-RTT,回访客户端可以做到 0-RTT(第一个往返就带数据)。对照 TCP + TLS 的经典流程,省掉的是实打实的往返。
③ 连接迁移。 TCP 用「源 IP + 源端口 + 目的 IP + 目的端口」四元组标识连接,网络一换 IP 就断。QUIC 用连接 ID 标识,手机从 Wi-Fi 切到蜂窝网络时连接不断——对移动端是实打实的体验改善。
④ 加密内建。 TLS 1.3 是 QUIC 握手的一部分,不是叠加在上面。没有明文模式,元数据也被保护得更多。
三、代价与当下采用情况
CPU 更贵。 内核里的 TCP 栈经过了几十年的优化(分段卸载、内核 TLS),而 QUIC 多在用户态实现,每个包都要付出系统调用与加解密的开销。业界已有成熟的缓解手段(UDP GSO/GRO、批量收发、网卡卸载),但每 Gbit 的 CPU 成本仍然高于带 kTLS 的 TCP。
观测变难。 传输头被加密意味着传统抓包几乎失效,没有导出密钥就看不到连接细节。替代方案是结构化日志(qlog 一类)加可视化工具——在出事之前就把它开起来,而不是等排障时才发现没有数据。
会静默失效。 如果边缘或客户端网络封了 UDP 443,客户端尝试 QUIC 超时后回落到 TCP,网站照常工作,日志里不会有任何提示。所以要把 h3 与 h2 的请求比例当成一级指标来监控——这个比例突然跌到零,几乎总是防火墙规则,而不是客户端 bug。
采用情况(截至 2026 年 10 月):站点能力侧的统计在四成左右,而实际请求中 HTTP/3 的占比约两成,HTTP/2 仍占多数。这个差距不是矛盾——统计站点能力与实际协商结果不同,而且大量非浏览器客户端(curl、SDK、CI)默认仍走 HTTP/1.1 或 HTTP/2。HTTP/3 目前更像一个「浏览器 + CDN 协议」,而不是机器对机器协议。
浏览器支持已经接近普及,主流版本数年前就默认开启。
四、业务侧几乎不用改
坦白说:前端基本不用改代码。
协商由服务端与浏览器完成:服务端通过响应头(如 Alt-Svc)告知支持 HTTP/3,浏览器下次尝试用 QUIC,失败就回落。前端的职责是验证与判断,不是实现:
- 确认链路是否真的走到了 HTTP/3(看协议列或响应头);
- 理解它对弱网与高丢包场景帮助最大,而在办公室光纤上中位数几乎无变化——不要拿办公室网络评估它的价值;
- 如果性能问题集中在 p95/p99 和移动端,开启它是划算的;如果问题出在资源体积或主线程,那它救不了。
服务端开启大致是这样(以 Nginx 为例,QUIC 支持自 1.25.0 起提供,截至 2026 年 10 月官方仍标注为实验性):
server {
listen 443 quic reuseport; # UDP
listen 443 ssl; # TCP,回落用
http2 on;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
}
验证是否真走通 HTTP/3,光看响应头不够,要分两端确认:
# 服务端:用 curl 探测是否支持 HTTP/3(需 curl 7.66+ 且编译带 HTTP/3)
curl -I --http3 https://example.com -s | head -n 1
# 浏览器侧更直接——从导航计时里读出本次实际协商到的协议
// 真实用户侧埋点:本次导航到底走了 h3 还是 h2(h1 基本看不到)
const nav = performance.getEntriesByType('navigation')[0]
// 'h3' = 跑在 QUIC 上;'h2' = 回落到了 TCP
sendBeacon('/rum-protocol', nav.nextHopProtocol)
把 h3 与 h2 的请求比例当成一级指标监控——它突然跌到零,几乎总是 UDP 443 被封,而不是客户端 bug。
两个要点:TCP 与 UDP 要同时监听(回落机制依赖它),Alt-Svc 头不能少(没有它浏览器不会尝试 HTTP/3)。另外要把 UDP 443 在防火墙上双向放通——这是最常见的「配好了却不生效」的原因。
浏览器侧确认是否真走了 h3,看 DevTools 的协议列不如读 Performance API 准:
// 需要服务端返回 Timing-Allow-Origin,否则字段为空
const [nav] = performance.getEntriesByType('navigation')
console.log(nav.nextHopProtocol) // 'h3' | 'h2' | 'http/1.1'
// 逐个资源看也行,能发现「主文档是 h3、子资源走了 h2」这类混跑
performance.getEntriesByType('resource')
.forEach((r) => console.log(r.nextHopProtocol, r.name))
五、和 HTTP/2、WebTransport 的关系
| 维度 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | QUIC(UDP) |
| 应用层队头阻塞 | 有 | 已解决 | 已解决 |
| 传输层队头阻塞 | 有 | 有 | 已解决 |
| 建连往返 | 多 | 1-RTT+ | 1-RTT / 0-RTT |
| 连接迁移 | 不支持 | 不支持 | 支持 |
| 加密 | 可选 | 事实必须 | 内建且强制 |
补充两点容易混淆的:
- HTTP/3 不需要也不应该关掉 HTTP/2。两者在 443 端口并存(TCP 上一个、UDP 上一个),客户端自己选,回落机制正是你要的行为;
- WebSocket over HTTP/3(RFC 9220)截至 2026 年 10 月还没有可用的浏览器实现,不要把它写进方案里当现有能力(详见《WebSocket 协议》)。
服务端开了不等于生效,UDP 443 通不通要单独探:
# 探测服务端是否真的提供 h3(需 curl 支持 HTTP/3)
curl -sI --http3 https://example.com -o /dev/null -w '%{http_version}\n'
# 返回 3 才是真的通了;UDP 443 被封时会静默回落到 2,所以必须单独探
六、什么时候才真的能感觉到
HTTP/3 不会让每个请求都变快,它主要改善丢包、高 RTT 与网络切换这些场景,网络本来就好的时候中位数差异很小。
QUIC 基于 UDP,但不可靠这个说法不成立——可靠传输由 QUIC 自己实现,UDP 只是承载方式。
前端也不用改代码才能用上,协商由服务端与浏览器完成;前端要做的是验证它真的生效了。
开了也不代表一定能生效:UDP 被封时会静默回落,必须监控 h3/h2 比例才能确认(截至 2026 年 10 月,采用率仍在爬坡)。
HTTP/3 解决的不是「让请求更快」,而是「让丢包不再拖垮整条连接」——收益集中在移动端的长尾,不在办公室的中位数。
协议细节直接读 RFC 9114,流与连接的关系那部分比任何科普都准确。