
HTTP/3 与 QUIC:换掉 TCP 之后,网络真的更快了吗?
HTTP/3 最容易被说成一句话:「HTTP 改用 UDP,所以更快。」
这句话不能说完全错,毕竟 HTTP/3 的确运行在基于 UDP 的 QUIC 之上;可它省略的部分,恰好比留下来的更多。UDP 没有可靠传输、拥塞控制、连接状态和加密。倘若只把 TCP 删掉,再把 HTTP 报文扔进 UDP 数据报,得到的不会是下一代 Web,而是一台更快丢数据的机器。
真正承担工作的,是 QUIC。它在用户空间重新提供可靠传输、流量控制、拥塞控制、丢包恢复和连接管理,把 TLS 1.3 紧密地纳入握手,并用彼此独立的流承载应用数据。HTTP/3 则定义如何在 QUIC 之上表达 HTTP 语义。
所以问题不是「UDP 为什么比 TCP 快」,而是:为什么 HTTP 已经在 TCP 上演进了三十年,最后还要换掉脚下的传输层?换完以后解决了什么,又付出了什么?
一、从 HTTP/1.1 的排队说起
HTTP/1.1 默认使用持久 TCP 连接,多个请求可以复用连接,避免每个资源都重新握手。可同一连接上的响应仍需要按顺序处理。管线化虽然允许连续发送请求,却无法让后面的响应越过前面的响应。一个耗时请求挡住整条队伍,这就是应用层的队头阻塞 (Head-of-Line Blocking)。
浏览器长期采用的绕法,是向同一来源建立多条 TCP 连接。它能并行加载资源,却增加握手、慢启动和服务器状态;域名分片、文件合并、雪碧图等技巧,也都在用应用复杂度补协议限制。
HTTP/2 改用二进制帧和流。一条连接可以承载许多并发流,流的帧交错传输,再按流 ID 还原。某个响应很大,不再要求另一个响应等它全部发送完。HPACK 压缩重复头部,优先级机制帮助调度,Web 终于不用靠多开 TCP 连接制造并行。
好戏却没有就此结束。
HTTP/2 解决了 HTTP 层的排队,底下仍是一条有序、可靠的 TCP 字节流。假设某个 TCP 段丢失,即使后续段已经到达,操作系统也必须等待缺口补齐后,才能把连续字节交给 HTTP/2。由于 TCP 看不见 HTTP/2 的流边界,一个流的丢包会暂停这条连接上所有流的数据交付。
这叫传输层队头阻塞。它在低丢包、低延迟网络中未必显眼,在移动网络、跨洲链路或拥挤 Wi-Fi 上却会放大延迟。若再用多条 TCP 连接规避,HTTP/2 的单连接复用优势又被削弱。
二、QUIC 到底是什么?
QUIC 最初由 Google 推动,随后进入 IETF 标准化。2021 年发布的 RFC 9000 定义 QUIC 版本 1。规范特意说明,QUIC 是名字,不是缩写,所以不必努力给每个字母找一个单词。
从协议栈看,它位于 UDP 与应用协议之间:
HTTP/1.1、HTTP/2:HTTP → TLS → TCP → IP
HTTP/3: HTTP/3 → QUIC(含 TLS)→ UDP → IP说 QUIC「基于 UDP」,主要是说它把 QUIC 包封装进 UDP 数据报,以便穿过现有操作系统与网络设备。可靠性不是 UDP 赠送的,而是 QUIC 自己实现的。QUIC 是面向连接、有状态的通用传输协议,提供:
- 多条有序的单向或双向流
- 确认、丢包检测与重传
- 连接级和流级流量控制
- 拥塞控制与 ECN 支持
- 基于 TLS 1.3 的机密性与完整性保护
- 低延迟连接建立和 0-RTT
- 使用连接 ID 支持连接迁移
UDP 更像一张通行证。TCP 在操作系统内核中已经存在几十年,中间设备还会依据 TCP 头、序列号和状态做大量判断。直接部署一种新的 IP 传输协议,很可能被 NAT、防火墙和负载均衡器拦在门外;复用 UDP 则更容易抵达应用程序,让协议在用户空间迭代。
这里有个重要取舍:QUIC 并没有消灭 TCP 的复杂性,而是把适合现代加密与多路复用的那部分传输能力重新组合,并更多地搬到用户空间。
三、连接是怎么建立的?
传统 HTTPS 通常先完成 TCP 三次握手,再进行 TLS 握手。TLS 1.3 已将完整握手压缩到一次往返,但两层握手按顺序发生,新连接仍需等待。
QUIC 将传输参数协商与 TLS 1.3 握手结合。客户端的 Initial 包已经携带 TLS ClientHello,服务器在回应传输参数和证书的同时建立加密上下文。一次往返后,客户端通常就可以发送受 1-RTT 密钥保护的应用数据。
Initial 包并不是明文裸奔。QUIC 使用公开可推导的初始密钥提供包保护,以避免协议元数据被随意篡改;但这种初始保护不对知道连接信息的观察者构成真正机密性,服务器身份仍由 TLS 握手验证。后续握手与 1-RTT 数据使用各阶段密钥保护,QUIC 会加密尽可能多的头部信息,并对整个包进行认证。
0-RTT 为什么不能随便用?
客户端曾与服务器通信并持有可恢复会话信息时,可以在完整确认服务器仍然是谁之前发送 0-RTT 应用数据。这减少一次往返,对高延迟链路很诱人。
代价是 0-RTT 数据可能遭到重放。攻击者不必解密请求,只要再次发送捕获的数据,就可能让服务器重复执行操作。因此,查询静态资源等幂等请求相对适合 0-RTT;创建订单、转账、修改密码之类有副作用的操作不能只因传输更快就放弃重放防护。服务端可以拒绝早期数据,客户端也必须能够在握手完成后重发。
低延迟不是免费的。它用「利用之前连接积累的信任」换取时间,也把重放判断交给应用语义。
四、QUIC 怎样避开队头阻塞?
QUIC 的可靠交付单位围绕流组织。每条流内部仍然保持字节顺序;否则应用收到一段乱序 JSON,也很难感到快乐。但不同流之间没有必须共同等待的交付顺序。
假设一个 QUIC 包包含流 A 的数据并丢失,之后承载流 B 的包已经到达。接收端可以继续向应用交付流 B,只有流 A 等待缺失范围重传。这正是 HTTP/3 相比 HTTP/2 在丢包网络中的关键改善。
注意两个边界。
第一,QUIC 没有消灭流内部的队头阻塞。单个大响应的一部分丢失,这个响应后续有序数据仍然要等。解决的是「一个流丢包拖住所有其他流」,不是凭空取消顺序。
第二,网络拥塞是连接共享的。丢包若被判断为拥塞,拥塞窗口会收缩,所有流可发送的总量都受影响。因此别把 QUIC 描述成「一个流丢包,其他流完全无感」。它们不再被可靠交付顺序强制卡住,但仍共同面对同一条网络路径的容量。
QUIC 还分别提供连接级与流级流量控制。接收端通过额度限制对方最多发送多少数据,避免某条流或整条连接无限占用内存。流量控制保护接收方处理能力;拥塞控制保护网络,两者解决的不是同一个问题。
五、QUIC 重传的不是包
TCP 使用序列号标记字节范围。QUIC 则给每个包分配包号,并用帧承载 STREAM、ACK、流量控制等信息。包号只增不复用;丢失包里的信息会在新包、新包号中重新发送。
这带来一个更清晰的语义:收到某个包号的 ACK,就是确认那个特定包,而不是猜测「这是原包还是重传包」。QUIC 的 ACK 帧可以表示多个已接收区间,帮助发送方判断缺口。丢包检测与拥塞控制由 RFC 9002 说明,其中给出了基于往返时间和包阈值的恢复机制,以及一个示例拥塞控制算法。
为什么不直接重发原包?因为包里的不同帧可能有不同命运:某些控制信息已不再需要,某条流也许已被取消,真正需要重传的是尚未被确认的语义数据。把「包」和「待可靠交付的信息」分开,给实现更多调度空间。
QUIC 没规定全世界只能使用某一种拥塞控制算法。实现可以选择适合环境的算法,但必须公平地响应拥塞,不能仗着跑在 UDP 上就横冲直撞。UDP 不代表不讲规矩;若新协议不做拥塞控制,它会伤害同一路径上的所有流量。
六、HTTP/3 在 QUIC 上加了什么?
RFC 9114 定义了 HTTP/3。HTTP 的方法、状态码、URI 和大部分语义没有推倒重来。变化主要发生在如何把这些语义映射到 QUIC 流和帧上。
客户端发起的双向 QUIC 流通常承载一个请求及对应响应。HTTP/3 定义 HEADERS、DATA 等帧,帧存在于 QUIC 流中;QUIC 自己的 STREAM、ACK、MAX_DATA 等传输帧则属于下一层。两种「帧」不要混为一谈。
一个连接还有用于 HTTP/3 控制、QPACK 编码器和解码器的单向流。控制流交换 SETTINGS 等连接级信息。由于流彼此独立,HTTP/2 中某些依赖全连接顺序的设计也必须调整。
QPACK 为什么不继续用 HPACK?
HTTP/2 的 HPACK 使用静态表与连接级动态表压缩重复头字段。编码器可以引用动态表中之前插入的条目,但解码器必须按正确顺序处理这些更新。在单一 TCP 字节流上,顺序天然存在。
到了 QUIC,不同流可能乱序到达。若直接照搬依赖顺序的动态表机制,一个包含头部的请求流可能等另一条流上的表更新,重新制造跨流阻塞。
QPACK 为 HTTP/3 重新设计了头部压缩。它使用独立的编码器、解码器流同步动态表,并允许实现通过阻塞流数量等设置,在压缩率与队头阻塞风险之间取舍。只使用静态表或字面值可以避免动态表依赖,却会牺牲压缩效果。
协议设计很少有「既要又要还全免费」。QPACK 做的,是让这个取舍可以被明确管理。
服务器推送还重要吗?
HTTP/3 仍定义服务器推送,但现实中的浏览器生态已经对推送相当克制,部分实现甚至不再支持。原因不难理解:服务器未必知道客户端缓存里有什么,贸然推送可能浪费带宽,还会与浏览器自己的优先级和缓存判断打架。
这提醒我们,规范提供能力,不代表业务必须使用。想优化首屏,缓存策略、资源优先级、preload、减少阻塞资源往往比「把所有东西推过去」更可靠。
七、换网络后,连接为什么还能继续?
TCP 连接通常由源地址、源端口、目的地址、目的端口等信息识别。手机从 Wi-Fi 切到 5G,IP 地址和 NAT 映射改变,旧 TCP 连接往往失效,只能重新连接。
QUIC 使用连接 ID 将连接身份与具体网络路径分离。客户端地址变化后,可以在新路径发送数据,通过 PATH_CHALLENGE 与 PATH_RESPONSE 验证对方确实能够接收该地址的数据,然后迁移现有连接。拥塞控制还需要谨慎对待新路径,不能把旧路径积累的容量判断原封不动搬过去。
这对移动设备很有价值:切换网络不一定意味着正在进行的请求全部重来。它也带来隐私问题——一个长期不变的连接 ID 可能帮助旁观者关联用户在不同网络上的活动。因此 QUIC 支持发放、轮换和停用连接 ID,实现与运营方还需避免把可关联标识保持得过久。
连接迁移也不是「任何切网都无缝」。新路径可能屏蔽 UDP、MTU 不同或验证失败,客户端和服务器实现也有策略限制。协议提供了恢复能力,不代表物理网络会配合演出。
八、安全被放进了传输层
TCP 可以明文使用,也可以在上面叠 TLS。QUIC 版本 1 则要求 TLS 1.3,其握手与包保护属于协议不可拆分的一部分。RFC 9001 定义了 QUIC 如何使用 TLS 协商密钥。
默认加密减少了中间设备窥视和篡改传输元数据的空间,也避免把不加密的 HTTP/3 当作正常部署方式。不过,加密不等于看不见一切。IP 地址、UDP 端口、包大小、时序以及部分必要头部仍可能暴露,流量分析依然存在。终端、反向代理和应用日志也仍能看到请求内容。
UDP 还带来地址伪造和反射放大风险。服务器在验证客户端地址前,发送的数据量受到严格限制,通常不得超过收到字节数的三倍;必要时服务器可发送 Retry,要求客户端带着令牌重试,以证明它确实能在声明的地址接收数据。
QUIC 把更多传输头部加密,也让传统依赖五元组与 TCP 状态的运维、安全设备看得更少。组织必须用终端指标、qlog、负载均衡器日志和应用遥测补上可观测性,不能指望在中间链路抓个包就读懂全部状态。
九、部署 HTTP/3 为什么不是开个开关?
HTTP/3 通常使用 UDP 443,但客户端第一次访问时未必知道来源支持它。服务器可以通过 HTTPS 响应中的 Alt-Svc 头告知客户端,也可以通过 DNS HTTPS 资源记录提供服务信息。客户端随后尝试 QUIC,并缓存结果。
Alt-Svc: h3=":443"; ma=86400生产部署要检查整条路径:CDN、负载均衡器、防火墙、NAT、主机 UDP 缓冲、证书、访问日志和监控是否都理解 QUIC。只在源站启用而边缘设备不转发 UDP,不会产生 HTTP/3,只会产生困惑。
还要准备回退。部分企业网络、校园网或旧设备会限速、丢弃 UDP。客户端通常会在 QUIC 失败时回退到基于 TCP 的 HTTP/2 或 HTTP/1.1。若实现让用户等待很久才回退,理论上的低延迟反而变成更慢的首次访问。
UDP 就一定更吃 CPU 吗?
TCP 在内核中经过多年优化,拥有成熟的分段卸载、合并、拥塞控制和网卡支持。QUIC 大量工作位于用户空间,又必须执行加密,早期或朴素实现确实可能消耗更多 CPU。现代系统已经加入 UDP 分段与接收合并等能力,QUIC 库也在持续优化,但结果高度依赖内核、网卡、实现和包大小。
因此评估不能只看页面加载时间,还要看每请求 CPU、每字节成本、丢包下吞吐、长尾延迟和服务器最大并发。CDN 边缘可能很划算,小型源站却未必能在同样硬件上免费获得收益。
MTU 与黑洞
QUIC 运行在 UDP 上,必须面对路径 MTU。RFC 9000 要求承载 Initial 包的 UDP 数据报有效载荷至少为 1200 字节,并定义了相关探测和处理规则。网络若错误丢弃较大的 UDP 数据报或屏蔽必要 ICMP 信息,可能形成难排查的黑洞。
实现需要正确做数据报分组层路径 MTU 探测,运营侧也应观察特定网络和地区的握手失败率。不能在办公室 Wi-Fi 打开首页一次,就宣布全球用户已经被优化。
十、HTTP/3 什么时候真的更快?
HTTP/3 的优势最容易出现在:
- 网络往返时间较高,减少握手等待更有价值
- 丢包会明显影响 HTTP/2 单连接上的多条流
- 用户频繁在 Wi-Fi 与移动网络之间切换
- 页面包含许多可并发请求,跨流独立交付能发挥作用
- 客户端与边缘节点都已有成熟 QUIC 实现,且 UDP 路径畅通
它不保证所有场景变快。低延迟局域网里,一个复用已久的 HTTP/2 连接可能和 HTTP/3 没有可感知差距;只有一个大文件时,跨流队头阻塞优势也无从发挥;CPU 紧张、UDP 被限制或回退策略糟糕时,HTTP/3 甚至可能更慢。
更别把协议升级当作前端性能赎罪券。几兆 JavaScript、未压缩图片、串行 API、错误缓存和慢后端,不会因为换了 QUIC 就获得无罪释放。传输层只能优化自己负责的那段路。
部署后应分协议观测:
- HTTP/3 协商成功率与回退率
- 首次连接、恢复连接的握手耗时
- 按地区、运营商划分的丢包与 RTT
- 请求 P50、P95、P99 延迟,而非只有平均值
- 服务器 CPU、内存和每字节处理成本
- 连接迁移次数、失败原因和异常关闭码
最好采用逐步放量,让同一时期、相似用户的 HTTP/2 与 HTTP/3 数据可比较。协议性能高度依赖网络条件,跨月份对比很容易把流量变化当成技术收益。
十一、几个常见误解
「QUIC 是把 TCP 搬到 UDP 上」
不准确。QUIC 确实重新实现了可靠性和拥塞控制,却提供原生多流、连接 ID、集成 TLS、多个包号空间等不同抽象。把它当成「用户态 TCP」会漏掉设计核心。
「HTTP/3 解决了所有队头阻塞」
没有。它避免一个流的丢失阻塞其他流的有序交付;单流内部仍有顺序,拥塞窗口仍由连接共享,QPACK 动态表依赖也可能造成受控阻塞。
「用了 UDP 就不会重传」
不会自动重传的是 UDP。QUIC 会确认和重新发送尚未可靠交付的信息。视频或游戏若需要不可靠数据报,还可以使用 QUIC DATAGRAM 扩展,但那不是 HTTP/3 请求响应的默认语义。
「QUIC 完全无法被网络管理」
加密确实减少中间盒能读取的细节,但端点仍可以导出指标和 qlog,网络仍可观察地址、流量大小与时序。改变的是可观测位置:从随意解析中间报文,更多转向由端点明确提供遥测。
「打开 HTTP/3 就应该关闭 HTTP/2」
恰恰相反。现实网络不会整齐升级。保留 HTTP/2 和必要的 HTTP/1.1 回退,是部署 HTTP/3 的正常组成部分,不是失败证明。
十二、一次更像工程的升级
如果要为现有网站启用 HTTP/3,我会先确认客户端流量是否足够现代、用户是否处于高 RTT 或高丢包网络,以及当前瓶颈是否真在传输层。若核心问题是后端查询 3 秒,先修数据库。
然后在 CDN 或边缘代理小比例启用,确保 UDP 443、证书、Alt-Svc、日志和监控贯通。保留 TCP 回退,收集按网络和协议划分的握手、失败、延迟与资源成本。观察完整业务周期后再扩大流量。
若效果有限,也不必强行写一份「提升 300%」的战报。协议升级的收益有时是移动网络下更稳、更少长尾,而不是实验室里平均值下降一大截;有时它确实不值额外运维成本。承认边界,比挑一组最漂亮的数据更诚实。
回到开头:HTTP/3 并不是因为 UDP 天生比 TCP 快。它真正的进步,是把加密、握手、多流和连接迁移作为一个整体重新设计,让独立的应用流不再被一条内核字节流的缺口一起拦住。
代价同样真实:用户态实现更复杂,CPU 和可观测性模型发生变化,UDP 并非处处畅通,0-RTT、连接迁移与地址验证各有安全边界。
QUIC 没有让网络摆脱取舍。它只是把旧取舍重新摆上桌,给现代互联网一次更合适的选择。至于你的服务要不要选,别听宣传页,去看真实用户的那条链路。
