解密 HTTP/2 与 HTTP/3 的新特性
Category(分类): NET Protocol Status: 未知
HTTP/2 相比 HTTP/1.1 在二进制分帧、首部压缩和多路复用等方面进行了改进,通常可以减少请求排队和重复首部带来的开销。但 HTTP/2 并不是在所有场景下都一定更快,也不能完全替代前端性能优化。HTTP/3 则将 HTTP 语义运行在 QUIC 之上,主要解决 HTTP/2 依赖 TCP 时产生的连接级队头阻塞,并提供连接迁移等能力。
HTTP/1.1 发明以来发生了哪些变化?
如果观察某一时期流行网站首页需要下载的资源,会发现页面资源数量和传输数据量都曾经持续增长。原文引用的统计图显示,在特定样本和时间范围内,页面传输数据量曾超过 2100 KB,平均资源数量也曾超过 100 个。
这些数字属于特定时期和统计样本,不能直接当作今天所有网页的普遍数据。不过,网页从早期以文本为主逐渐发展为包含图片、音频、视频和实时交互内容的富媒体应用,确实对协议的连接复用、传输效率和安全性提出了更高要求。

HTTP/1.1 于 1997 年发布,长期以来一直被广泛使用。随着页面资源数量增加,HTTP/1.1 在高延迟网络和大量小资源场景下的局限逐渐显现,HTTP/2 和 HTTP/3 因此得到发展。
一、HTTP/1.1 的常见问题
HTTP/1.1 本身并不是不可用的协议,但在大量小资源、较高 RTT 或存在丢包的网络环境中,以下问题可能限制性能:
- 同一连接上的请求排队和队头阻塞;
- 缺少通用的动态首部压缩,重复首部带来额外开销;
- HTTP 协议本身不提供加密和身份认证;
- 没有 HTTP/2 那样的标准服务器推送流机制。
1. 高延迟与队头阻塞
网络带宽增长并不意味着网络延迟会同比降低。网络延迟还会受到物理距离、路由、拥塞、DNS、连接建立和服务器处理时间等多种因素影响。HTTP/1.1 中,同一持久连接上的请求可能需要排队,队首请求没有完成时,后续响应也可能受到影响,这就是应用层的队头阻塞(Head-of-Line Blocking,HOL Blocking)。

HTTP/1.1 支持管道化(pipelining),客户端可以在没有收到前一个响应时继续发送后续请求,但响应必须按照请求顺序返回,队首响应延迟仍会阻塞后续响应。由于中间设备和浏览器对管道化的支持、兼容性和部署体验不理想,现代浏览器通常更多使用多个持久连接提高并发度。
旧版浏览器常见的做法是限制同一主机的并发连接数,例如曾经常见的 6 个连接。但具体数量会随浏览器、协议版本、代理和配置变化,不能当作固定标准。HTTP/2 和 HTTP/3 通常可以在一个连接上复用多个流,也不应继续把“同域名固定 6 个连接”作为现代浏览器规则。
为了缓解 HTTP/1.1 下的请求排队,过去常见的做法包括:
- 域名分片:将资源分散到不同域名,以绕过单个主机的连接数限制。但在 HTTP/2 和 HTTP/3 中,域名分片可能破坏连接复用,增加握手和拥塞控制开销,需要根据实际情况评估。
- Spriting(雪碧图):将多张小图合并为一张大图,再通过 CSS 或 JavaScript 定位使用,可以减少请求数,但会降低资源缓存的粒度。
- 内联(Inlining):将小图片等资源编码后嵌入 CSS、HTML 或脚本中,减少请求次数,但可能增大主文档并影响缓存。
例如,图片可以以内联数据的形式写入 CSS:
.icon1 {
background: url(data:image/png;base64,<data>) no-repeat;
}
.icon2 {
background: url(data:image/png;base64,<data>) no-repeat;
}
- 拼接(Concatenation):将多个较小的 JavaScript 文件打包为一个文件,可以减少请求数。但其中一个文件发生修改时,整个合并文件可能需要重新下载。现代构建工具通常会结合代码分割和长期缓存使用。
这些优化是特定网络条件下的工程手段,并不是所有项目都应该无条件采用。
2. 无状态语义与首部开销
HTTP 的无状态性是指每个请求在语义上相对独立,服务器不能只依赖上一个请求来理解当前请求。这并不意味着每个请求都要新建 TCP 连接:HTTP/1.1 默认支持持久连接,同一 TCP 连接可以承载多个请求。
请求头中的 User-Agent、Cookie、Accept 等字段,以及响应头中的 Cache-Control、Content-Type、Server 等字段,可能在很多报文中重复出现。对于请求体很小的场景,首部占比可能较高,从而增加传输成本。
需要注意,Server 通常是响应头,不应作为请求头示例。204 No Content 和 304 Not Modified 按规范不能包含响应内容,301 响应则可以包含可选的响应内容,因此不能把它们都简单归类为“只有几十字节的响应”。
HTTP/1.1 没有像 HTTP/2 的 HPACK、HTTP/3 的 QPACK 那样的通用动态首部压缩机制,因此重复首部的开销更明显。Cookie 还可能随着业务信息增加而变大,实际应用中应控制 Cookie 的数量和大小。

3. HTTP 本身不提供加密
HTTP/1.1 的协议语义本身不提供加密、完整性保护和身份认证。直接使用 http:// 时,网络中的攻击者可能窃听或篡改内容,也无法通过 HTTP 本身确认服务器身份。
例如,公共 Wi-Fi 环境中的攻击者可能通过伪造热点或中间人攻击观察未加密的 HTTP 流量。如果请求中包含密码、令牌等敏感信息,就可能造成泄露。
HTTP/1.1 也可以运行在 TLS 之上,即 HTTPS。HTTPS 通过 TLS 提供加密、完整性保护,并通常验证服务器证书。因此,不能简单地说所有 HTTP/1.1 都是明文,应区分 HTTP 协议语义和承载它的 TLS 连接。
4. 没有标准化的服务器推送
HTTP/1.1 没有 HTTP/2 那样的标准服务器推送流机制。服务器可以使用缓存预热、Link: preload、103 Early Hints 等方式帮助客户端提前发现资源,但这些机制与 HTTP/2 Server Push 并不完全相同。
二、SPDY 协议与 HTTP/2 简介
1. SPDY 协议
由于 HTTP/1.x 在高延迟网络中存在请求排队和首部重复等问题,Google 在 2009 年公开了 SPDY 协议。SPDY 在 HTTP 语义之下引入了二进制分帧、流复用、首部压缩和优先级等机制,通常运行在 TCP 和 TLS 之上。

SPDY 的实践验证了这些设计思路的可行性,后来 HTTP/2 吸收了其中很多特性。SPDY 本身已经退出历史舞台,现代实现主要使用 HTTP/2 或 HTTP/3。
HTTP/1.1 的主要问题可以概括为性能和安全方面的局限。由于 HTTP/1.x 背负着庞大的历史包袱,协议升级需要考虑兼容性,否则可能影响互联网上已有的大量客户端、服务器和中间设备。
2. HTTP/2 简介
HTTP/2 于 2015 年以 RFC 7540 的形式发布。当前 HTTP/2 的主要规范是 RFC 9113,HPACK 仍由 RFC 7541 定义。
HTTP/2 不是把 HTTP 语义完全重写,而是尽量保留 HTTP 方法、状态码、请求头、响应头和内容等语义,同时改变线上报文的传输格式。HTTP/2 的二进制帧格式与 HTTP/1.1 的文本报文格式并不兼容,需要通过 ALPN、Upgrade 或其他协商方式确定协议版本。
HTTP/2 的主要目标之一是允许同一连接承载多个并发请求和响应,但协议并不强制每个 origin 只能建立一个连接。客户端和服务器仍可能因为并发流限制、连接策略、故障隔离或其他原因使用多个连接。
HTTP/2 的性能收益没有统一固定值,实际效果取决于网络 RTT、丢包率、资源数量、资源大小、缓存、服务器配置和客户端实现。
三、HTTP/2 的新特性
1. 二进制分帧
HTTP/2 使用二进制帧传输协议控制信息和 HTTP 消息,不再直接传输 HTTP/1.1 那样的文本请求行和首部行。二进制格式主要有利于解析、边界识别和多路交错传输,并不意味着所有数据都会因为二进制编码而自动变小。
HTTP/2 常见的帧包括:
HEADERS:传输首部块;CONTINUATION:延续较大的首部块;DATA:传输请求或响应内容;SETTINGS:协商连接参数;WINDOW_UPDATE:更新流量控制窗口;RST_STREAM:终止单个流。
一个请求或响应可以由一个或多个消息部分组成,消息由一个或多个帧承载。帧带有流标识符,来自不同流的帧可以在同一个 TCP 连接中交错发送;但是同一个流中的帧仍需要按顺序处理,首部块中的 CONTINUATION 帧也必须保持规定的连续顺序。
HTTP/2 仍然保留 HTTP 的首部和内容语义,只是将这些信息拆分到不同的二进制帧中,并不意味着“Header + Body”在语义上完全消失。

2. HPACK 首部压缩
HTTP/2 使用 HPACK 压缩首部。HPACK 不是简单地对每个请求独立使用 gzip,而是让编码端和解码端维护静态表和动态表,并用索引引用重复的首部字段。
HPACK 主要包括:
- 静态表:预先定义常见的首部名称和值;
- 动态表:连接过程中根据已发送的首部字段逐步更新,并受到表大小限制;
- 索引表示:直接引用表中的字段;
- 字面量表示:直接发送新的字段名称或值,可以选择是否加入动态表;
- Huffman 编码:可用于压缩字符串字面量,整数使用 HPACK 定义的变长整数编码。
动态表并不是永久不变的字典。新条目可能被插入,旧条目可能因为容量限制被逐出,编码端还可以选择不索引敏感字段。首部压缩比例取决于请求的重复程度和字段大小,没有固定的“50%~90%”保证。

3. 多路复用
HTTP/2 在一个连接中引入多个独立的流。每个请求/响应通常对应一个流,流中的消息被拆成帧后,可以与其他流的帧交错发送。这样可以减少 HTTP/1.1 中为提高并发而建立多个 TCP 连接的需要,并且通常只需要进行一次 TCP 慢启动和一次 TLS 建立。
HTTP/2 的多路复用具有以下特点:
- 一个连接可以承载多个双向流,但并发流数量受
SETTINGS_MAX_CONCURRENT_STREAMS等参数限制; - 一个流的 HTTP 层响应不必等待另一个流的响应完成;
- 连接级流量控制和流级流量控制共同限制发送量;
- 服务器仍需要根据资源、优先级和负载对各个流进行调度;
- TCP 仍然是单一有序字节流,因此 TCP 丢包会影响该连接中多个流的数据交付。

原文曾使用 Akamai 的 HTTP/2 演示链接展示 HTTP/2 与 HTTP/1.1 的差异:https://http2.akamai.com/demo。该链接属于历史演示,是否仍可访问取决于服务状态,实际性能应以当前项目的真实测试为准。
多路复用通常可以减少连接数和握手开销,但不代表所有请求都能无限并行,也不代表不同流之间完全互不影响。服务器处理能力、连接级窗口、拥塞控制和 TCP 队头阻塞仍然会造成相互影响。
HTTP/2 优先级
早期 HTTP/2 使用流依赖关系和权重表达优先级。传统优先级机制包含流依赖关系、独占标志和 1~256 的权重,并不是“每个请求拥有一个 31 位优先值,数值越小越优先”。
当前 RFC 9113 已经弃用原始的优先级依赖树机制,现代实现可以使用 HTTP Extensible Priorities(RFC 9218)中的 Priority 首部等方式表达紧急程度和增量处理偏好。优先级只是调度建议,不能保证服务器一定按照某种顺序发送。

4. Server Push
HTTP/2 规范定义了 Server Push。服务器可以在客户端请求 HTML 后,通过 PUSH_PROMISE 预先声明并发送客户端可能需要的资源,例如 CSS 或 JavaScript,从而减少客户端解析 HTML 后再发起请求的等待时间。
客户端可以通过设置禁用推送,也可以使用 RST_STREAM 取消不需要的推送。服务器推送还必须考虑缓存状态、资源是否真的需要以及带宽竞争问题;错误的推送可能浪费带宽,甚至拖慢真正重要的资源。
规范层面仍然可以描述 Server Push,但 Chrome 等主流浏览器已经基本停止支持或默认关闭该功能,现代网站很少把它作为主要性能优化手段。当前更常见的替代方式包括合理的缓存、预加载、103 Early Hints 和资源优先级控制。

5. HTTP/2 与安全性
HTTP/2 的二进制格式本身不等于加密。规范允许使用明文 HTTP/2,协议标识为 h2c;但主流浏览器通常只在 TLS 上使用 HTTP/2,使用 ALPN 协商的加密协议标识为 h2。
因此,现实中浏览器访问的 HTTP/2 网站通常是 HTTPS,但安全性主要来自 TLS,而不是来自 HTTP/2 的二进制分帧。部署 HTTP/2 时还应正确配置证书、TLS 版本、密码套件和 ALPN。

四、HTTP/3 的背景与新特性
1. HTTP/2 的局限
HTTP/2 解决了 HTTP/1.1 的部分应用层问题,但它通常仍运行在 TCP 之上,因此继承了 TCP 的连接级特性。主要局限包括:
- TCP 和 TCP+TLS 的连接建立延迟;
- TCP 的连接级队头阻塞;
- 大量并发流带来的服务器资源压力。
连接建立延迟
使用 HTTPS 的 HTTP/2 连接通常涉及 TCP 和 TLS 两个阶段:
- TCP 三次握手通常按约 1 个 RTT 估算。客户端在收到 SYN+ACK 后可以把数据与最后的 ACK 一起发送,但服务器仍需要等待 TCP 状态建立;
- TLS 1.2 完整握手通常还需要约 2 个 RTT,恢复连接时可能更少;
- TLS 1.3 完整握手通常还需要约 1 个 RTT,恢复会话时可以发送 0-RTT 早期数据,但早期数据存在重放风险。
因此,不能笼统地说 HTTP/2 一定需要 3~4 个 RTT。实际延迟还受到 DNS、代理、证书验证、连接复用、缓存和网络拥塞等因素影响。
RTT(Round-Trip Time) 表示信号或报文从一端到另一端并返回所经历的往返时延。它不一定等同于“发送数据后立即收到 ACK 的时间”,因为 TCP 可能延迟确认,应用层响应也可能晚于传输层 ACK。
TCP 层的队头阻塞
HTTP/2 的多个流共享一个 TCP 连接。TCP 提供的是有序字节流,当某个 TCP 报文段丢失时,即使后续报文已经到达,TCP 也通常要等待缺失数据重传并恢复顺序后,才会继续向 HTTP/2 交付后续字节。
因此,一个 TCP 丢包可能暂时阻塞同一连接中的多个 HTTP/2 流。这是 HTTP/2 没有彻底解决的 TCP 层队头阻塞。HTTP/1.1 使用多个连接时,可以把影响限制在某一个连接中,但会增加连接、TLS 和拥塞控制开销。
RTO(Retransmission Timeout)是重传超时时间。TCP 会根据连接的 RTT 和波动情况动态计算 RTO;TCP ACK 通常表示接收方希望收到的下一个字节序号,而不是简单表示“下一组数据包的序号”。

TCP 已经部署在大量操作系统、网络设备和中间盒中,不能轻易改变其基本的有序字节流语义。但 TCP 仍然可以通过选项和拥塞控制算法持续演进,HTTP/3 则选择在 UDP 之上重新实现一套适合多路复用的可靠传输协议 QUIC。
2. HTTP/3 简介
HTTP/3 是将 HTTP 语义运行在 QUIC 之上的协议。QUIC 使用 UDP 作为底层数据报承载,但在 UDP 之上实现了可靠传输、拥塞控制、流量控制、加密握手和多路复用。
HTTP/3 已经完成标准化,主要规范是 RFC 9114;QUIC 的传输规范是 RFC 9000。HTTP/3 并不是简单地把 HTTP/2 的帧放进 UDP,而是使用 QUIC 的流、帧和 TLS 1.3 机制重新组织传输。

QUIC 主要解决的是 TCP 连接级队头阻塞和连接迁移问题,但并不保证所有网络环境下都比 HTTP/2 快。UDP 可能受到网络设备限速、丢包和部署策略影响,最终性能仍取决于具体网络和实现。
3. QUIC 的主要特性
3.1 在 UDP 之上提供可靠传输
UDP 本身不提供可靠性、连接状态、拥塞控制和有序字节流,但 QUIC 在 UDP 之上补充了这些能力。QUIC 流数据可以进行确认、丢包检测和重传,连接还具有流量控制和拥塞控制。
QUIC 并不保证数据在任何故障条件下“一定能够抵达”。如果连接断开、对端不可用或重传次数耗尽,应用程序仍会收到失败结果。
QUIC 的 Packet Number 用于识别数据包和进行丢包检测,流数据的顺序则由每个 Stream 内的偏移量维护。QUIC 丢包恢复时通常重新发送需要恢复的帧,并将它们放入具有新 Packet Number 的数据包中,而不是简单重传完全相同的 UDP 数据包。
3.2 快速握手与 0-RTT
QUIC 将 TLS 1.3 集成到连接建立过程中。新的 QUIC 连接通常需要约 1-RTT 完成握手;如果客户端拥有之前会话的恢复信息,则可能使用 0-RTT 发送早期应用数据。
0-RTT 不是首次连接都能使用,也不代表完整连接已经完成。早期数据发送后,TLS 和 QUIC 握手仍需要继续完成。0-RTT 数据具有重放风险,并且不具备完整的前向保密性,因此只适合幂等、可安全重复执行的请求,不适合直接承载支付、转账等不可重复操作。
TLS 1.3 的 1-RTT 数据通常具有前向保密性;0-RTT 数据使用恢复会话的 PSK 相关密钥,在密钥泄露时可能使攻击者解密之前捕获的 0-RTT 数据。服务器还应采取票据轮换、重放检测和请求限制等措施。
3.3 集成 TLS 1.3
QUIC 强制使用 TLS 1.3 进行握手和密钥协商。TLS 负责身份认证和加密,QUIC 负责传输层的包号、ACK、流量控制、丢包恢复和拥塞控制。两者共同完成安全可靠的传输。
3.4 基于流的多路复用
QUIC 允许同一连接包含多个相互独立的逻辑流。一个流中的数据丢失时,只会阻塞该流中需要按序交付的数据,其他流仍可以继续处理已收到的数据,从而避免 TCP 连接级队头阻塞。
但这并不意味着队头阻塞被“彻底解决”:
- 单个流内部仍然需要按序交付;
- 连接级流量控制和拥塞控制仍会影响所有流;
- HTTP/3 使用 QPACK 压缩首部时,某些动态表依赖也可能产生等待;
- 服务器和客户端的调度、CPU、内存及带宽仍然是共享资源。

3.5 连接迁移
传统 TCP 连接通常由客户端 IP、客户端端口、服务器 IP 和服务器端口组成的四元组识别。网络切换或 NAT 重绑定后,四元组可能发生变化,TCP 通常需要重新建立连接。
QUIC 使用 Connection ID 辅助识别连接。Connection ID 由连接端点发放和管理,长度可变,并不是固定的 64 位。只要满足握手已确认、路径验证通过以及其他迁移条件,客户端在切换网络或地址后可以继续使用同一个 QUIC 连接,而不必重新建立完整连接。
连接迁移并不是只要 Connection ID 不变就一定成功。QUIC 需要对新路径进行验证,服务器也需要遵守对新地址发送数据的限制。QUIC 的密钥上下文可以在迁移后继续使用,但仍可能发生密钥更新和路径相关的拥塞控制调整。
3.6 HTTP/3 的首部压缩
HTTP/3 不使用 HTTP/2 的 HPACK,而是使用为 QUIC 多流传输设计的 QPACK。QPACK 通过静态表、动态表和编码器/解码器流压缩首部,同时允许实现根据压缩效率和阻塞风险进行权衡。
五、HTTP/2 与 HTTP/3 的对比
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输格式 | 文本报文 | 二进制帧 | HTTP/3 帧运行在 QUIC 流上 |
| 底层传输 | TCP | TCP | UDP + QUIC |
| 首部压缩 | 无通用动态压缩 | HPACK | QPACK |
| 多路复用 | 依赖多个连接或管道化 | 同一 TCP 连接的多个流 | QUIC 连接的多个独立流 |
| TCP 连接级队头阻塞 | 每个连接分别存在 | 存在 | 由 QUIC 避免 |
| 加密 | 可使用 TLS | 浏览器通常使用 TLS | QUIC 强制集成 TLS 1.3 |
| 服务器推送 | 无标准推送流 | 规范支持,但浏览器实际使用很少 | 规范支持,但实际使用同样有限 |
| 连接迁移 | 通常需要重新连接 | TCP 本身不支持 | QUIC 支持路径验证后的迁移 |
六、总结
- HTTP/1.1 的主要性能问题包括同一连接上的请求排队、首部重复和连接建立开销;域名分片、雪碧图、资源内联等旧优化需要结合实际场景使用。
- HTTP/2 保留了 HTTP 的主要语义,但使用二进制分帧、HPACK 和多路复用提高传输效率。它通常可以减少 HTTP 层队头阻塞,但仍受到 TCP 连接级队头阻塞影响。
- HTTP/2 的 Server Push 仍存在于规范中,但已经不是主流浏览器普遍使用的性能手段。
- HTTP/3 将 HTTP 运行在 QUIC 之上,使用 QPACK、独立流、TLS 1.3、0-RTT 和连接迁移等能力,主要解决 TCP 连接级队头阻塞和网络切换问题。
- HTTP/2 和 HTTP/3 都不保证一定更快,最终效果取决于网络、资源、服务器、客户端、缓存和部署方式。