HTTP/1.1、HTTP/2 与 HTTP/3 的差异
1. 面试题
Q:HTTP/2 相比 HTTP/1.1 有哪些改进?HTTP/3 又解决了 HTTP/2 的什么问题?
2. 核心结论
HTTP/1.1、HTTP/2 和 HTTP/3 使用的 HTTP 方法、状态码和语义基本一致,主要区别在于:如何建立连接,以及如何在连接中传输多个请求和响应。
- HTTP/1.1:为了并发加载资源,浏览器通常会建立多条 TCP 连接,每条连接中的请求主要依次处理。
- HTTP/2:通常复用一条 TCP 连接,通过多个 HTTP/2 Stream 并发传输请求,并使用 HPACK 压缩请求头;但仍然存在 TCP 层的队头阻塞。
- HTTP/3:通常复用一条 QUIC 连接,通过多个 QUIC Stream 并发传输;一个 Stream 丢包等待重传时,不会阻止其他 Stream 继续交付已经收到的数据。
3. 核心对比
这里的连接数量都是常见实现,不是协议强制规定的固定数量。不同源、连接替换、代理和浏览器隔离策略都可能产生额外连接。
4. HTTP/1.1:通过多条连接实现并发
HTTP/1.1 默认支持持久连接,一条 TCP 连接可以被多个请求重复使用,并不需要每次请求都重新建立连接。
但是,HTTP/1.1 没有 Stream ID 来区分同一连接中的多个并发响应。它虽然支持请求管线化,可以连续发送多个请求,但服务器必须按照请求顺序返回响应,前面的响应较慢时,后面的响应仍然需要等待。因此浏览器实际通常不会依赖管线化,而是对同一个源建立多条 TCP 连接来实现并发:
HTTP/1.1 协议没有规定同一个源必须建立多少条连接。面试中经常提到的“浏览器同域名最多 6 条连接”只是常见实现策略,不是协议限制,具体数量可能因浏览器和运行环境而变化。
这种并发方式存在几个问题:
概括来说,HTTP/1.1 存在 往返延迟难以消除、连接级并发限制、应用层队头阻塞、多连接开销和 Header 冗余。这些问题会增加页面资源的整体加载时间,最终影响用户体验:
- 往返延迟难以消除:增加带宽可以提高数据吞吐量,却不能消除由传播距离和网络路径决定的 RTT。HTTP/1.1 加载大量资源时需要等待连接、请求和响应,这些往返过程会不断累积延迟。
- 连接级并发限制:浏览器对同一个源建立的并发连接数量有限,连接全部被占用后,超出的请求只能排队。常见的“最多 6 条连接”是浏览器实现策略,不是 HTTP/1.1 的协议规定。
- 应用层队头阻塞:同一连接通常需要完成前一个 HTTP 请求和响应后,才能可靠地处理下一个请求;慢请求会长期占用连接,使后续请求等待。
- 多连接开销:每条 TCP 连接都要独立建立并进行拥塞控制,还会经历慢启动;HTTPS 连接还需要完成 TLS 握手。建立更多连接虽然提高了并发度,也带来了额外的时间和资源成本。
- Header 冗余:HTTP 是无状态协议,每个请求都需要携带 Header。Cookie、User-Agent 等字段经常在多个请求中重复出现,Cookie 较大时尤其容易产生额外传输开销。
5. HTTP/2:一条 TCP 连接并发传输多个请求
HTTP/2 引入二进制分帧,把请求和响应拆成 HEADERS、DATA 等帧。每个请求和响应属于一个带有 Stream ID 的 HTTP/2 Stream,不同 Stream 的帧可以在一条 TCP 连接中交错传输:
HTTP/2 的主要改进
-
多路复用
多路复用可以直接理解为:一条连接同时承载多个 Stream。每个请求和对应响应使用一个独立的逻辑 Stream,但不需要为每个 Stream 重新建立 TCP 连接或进行 TLS 握手。
不同 Stream 的数据会被拆成带有 Stream ID 的帧,然后在同一条连接中交错传输:
接收方根据 Stream ID 把帧归类到对应的请求。这样多个请求可以同时发出,不需要等待某条连接空闲;某个请求的服务端处理较慢时,连接仍然可以传输其他 Stream 的数据。
-
二进制分帧
HTTP 消息被拆成可识别所属 Stream 的帧,使不同请求的数据能够在同一连接中交错传输。
-
HPACK Header 压缩
一个页面通常会请求 HTML、CSS、JavaScript、图片等大量资源,每个请求和响应都会携带 Header。其中 Cookie、User-Agent、Accept、Authorization 等内容经常重复出现;请求数量较多或 Cookie 较大时,反复发送完整 Header 会产生明显的额外开销。
HPACK 综合使用静态表、动态表、索引、字面量和 Huffman 编码。已经发送过的 Header 可以通过索引或只发送变化部分来表示,从而减少传输数据量,并不只是简单地“发送一个索引号”。
Header 压缩只负责压缩 HTTP 请求头和响应头,不负责压缩 HTML、JSON 等正文内容。正文通常由 gzip、Brotli 等内容编码负责压缩。
为什么 HTTP/2 一条连接还能比 HTTP/1.1 多条连接快?
连接数量不等于并发能力。HTTP/2 的一条连接可以包含很多并发 Stream,因此它通常有以下优势:
- 多个请求可以一起发出,减少等待可用连接的时间。
- 不同响应的数据可以交错传输,慢请求不会一直占用唯一的请求位置。
- 减少重复建立 TCP 和 TLS 连接的成本。
- HPACK 减少重复 Header 的大小。
- 所有请求共享已经建立并进入稳定状态的连接,连接利用率通常更高。
HTTP/2 仍然存在什么问题?
HTTP/2 的所有 Stream 最终都通过同一条 TCP 字节流传输。TCP 必须按照字节顺序向上层交付数据,如果中间一个 TCP 报文段丢失,即使后面的数据已经到达,也需要等待丢失的数据重传。
因此,一个 TCP 报文段丢失可能让这条连接中的所有 HTTP/2 Stream 暂时无法继续交付数据。这就是 HTTP/2 仍然存在的 TCP 层队头阻塞。
在高丢包网络中,HTTP/2 多路复用的优势可能下降,但不能直接得出 HTTP/1.1 一定更快的结论,因为 HTTP/1.1 的多条连接也会增加握手、拥塞控制和服务器资源成本。
Server Push 还需要重点讲吗?
HTTP/2 协议定义了 Server Push,服务端可以在客户端请求前主动推送可能需要的资源。但它很难准确判断浏览器是否已经缓存资源,实际收益不稳定,Chromium 已默认禁用该能力。
因此面试中可以把 Server Push 作为协议特性提及,但不应再把它作为现代 Web 项目的主要优化手段。当前通常优先考虑 Preload 或 103 Early Hints。
6. HTTP/3:使用 QUIC 减少跨流队头阻塞
HTTP/3 不再使用 TCP,而是使用基于 UDP 的 QUIC。这里的“基于 UDP”不代表 HTTP/3 不可靠:QUIC 自己实现了可靠传输、丢包重传、流量控制、拥塞控制和加密。
HTTP/3 通常对同一个源复用一条 QUIC 连接,并在连接中创建多个 QUIC Stream:
QUIC Stream 是什么?
QUIC Stream 是一条 QUIC 连接内部的逻辑数据通道,不是一条独立网络连接。创建 Stream 不需要重新进行网络连接或 TLS 握手。
它具有以下特点:
- 同一个 Stream 内的数据可靠并且按顺序交付。
- 如果一个 Stream 中间的数据丢失,这个 Stream 需要等待重传。
- 不同 Stream 的交付相对独立,一个 Stream 等待重传时,其他 Stream 可以继续交付数据。
- 所有 Stream 仍然共享连接的总带宽和拥塞控制;发生丢包后,整个连接的发送速度仍可能下降。
HTTP/3 的一个请求和对应响应通常使用一个客户端创建的双向 QUIC Stream。控制信息和 QPACK 状态则使用单向 Stream。
HTTP/3 的主要改进
-
减少跨 Stream 的传输层队头阻塞
某个 Stream 丢包时,主要由该 Stream 等待重传,不会像 TCP 那样要求其他 Stream 等待缺失的字节补齐后才能继续交付。
-
QPACK Header 压缩
HTTP/3 使用适配 QUIC 多 Stream 模型的 QPACK。它的目标与 HPACK 相同,都是减少重复 Header,但处理动态表和到达顺序的方式不同。
-
更快的连接建立
QUIC 将传输连接和 TLS 1.3 握手结合起来,首次连接通常可以用 1-RTT 建立。只有客户端和服务器之间存在之前连接保存的会话信息,并且服务器允许时,才能尝试使用 0-RTT 提前发送数据。
0-RTT 数据存在重放风险,因此并不是所有请求都适合使用,也不能把 HTTP/3 简单理解为“每次都是 0-RTT”。
-
连接迁移
QUIC 使用 Connection ID 识别连接,不完全依赖 IP 和端口。当设备从 Wi-Fi 切换到移动网络时,可以验证新路径并继续使用原来的 QUIC 连接。
HTTP/3 是否一定比 HTTP/2 快?
不一定。HTTP/3 在高延迟、存在一定丢包或网络切换的场景中通常更有优势,但也存在以下成本:
- QUIC 和加密处理更加复杂,可能增加 CPU 开销。
- 部分网络设备或防火墙可能限制 UDP,浏览器需要回退到 HTTP/2。
- 如果页面资源很少、网络稳定,HTTP/2 和 HTTP/3 的实际差距可能很小。
- 服务端和客户端实现质量也会影响最终性能。
7. 队头阻塞是如何逐步改善的?
HTTP/1.1
同一连接中的响应需要按照请求顺序返回。前面的响应慢,后面的响应也需要等待,因此存在应用层队头阻塞。浏览器通过建立多条连接缓解这个问题。
HTTP/2
多个请求通过不同 Stream 交错传输,解决了 HTTP/1.1 的应用层队头阻塞。但所有 Stream 仍然共享一条有序 TCP 字节流,因此仍有 TCP 层队头阻塞。
HTTP/3
QUIC 在传输层直接提供多个 Stream。单个 Stream 内仍然要求有序交付,但一个 Stream 等待丢包重传时,不会阻止其他 Stream 继续交付数据。
为什么 HTTP/2 会一起阻塞,而 HTTP/3 不会?
HTTP/2 的 Stream 只存在于 HTTP/2 层,底层 TCP 并不知道数据属于哪个 Stream。TCP 看到的是一条统一编号、必须按顺序交付的字节流:
即使数据包 2 和数据包 3 已经到达,TCP 也要等待数据包 1 重传后,才能继续按顺序向 HTTP/2 交付数据。因此 Stream 1 的丢包可能让 Stream 3 和 Stream 5 一起等待。
QUIC 则在传输层直接管理多个 Stream,并分别维护每个 Stream 的数据顺序:
所以 HTTP/3 避免的是不同 Stream 之间因为统一传输顺序产生的阻塞,并不是完全没有阻塞:
- 同一个 Stream 内仍然需要按顺序交付,丢失中间数据时,后续数据仍要等待。
- 所有 Stream 仍然共享连接带宽和拥塞控制,丢包可能让整个连接的发送速度下降。
可以总结为:
8. 高频追问
HTTP/1.1、HTTP/2、HTTP/3 分别建立几条连接?
- HTTP/1.1 为了并发通常对同一个源建立多条 TCP 连接,但数量由客户端决定,协议没有规定固定值。
- HTTP/2 通常对同一个源复用一条 TCP 连接,通过多个 HTTP/2 Stream 并发。
- HTTP/3 通常对同一个源复用一条 QUIC 连接,通过多个 QUIC Stream 并发。
这些都是常见模型,不是绝对限制。
QUIC Stream 与 QUIC 连接是什么关系?
QUIC 连接是真正维护网络状态、安全状态和拥塞控制的连接;QUIC Stream 是连接内部的数据通道。多个 Stream 共用连接资源,但分别维护各自的数据顺序和传输状态。
HTTP/3 是因为使用 UDP 才更快吗?
不是简单因为 UDP 更快。UDP 只为 QUIC 提供基础的数据报传输能力,真正的改进来自 QUIC 自己实现的多 Stream、集成 TLS 握手、连接迁移以及更灵活的传输控制。
9. 面试回答模板
HTTP/1.1 默认支持持久连接,但为了并发加载资源,浏览器通常需要对同一个源建立多条 TCP 连接,每条连接中的请求主要依次处理。HTTP/2 使用二进制分帧和多路复用,在一条 TCP 连接中通过多个 Stream 并发传输请求,并使用 HPACK 压缩 Header,因此能减少连接建立、请求排队和重复 Header 的开销;但所有 Stream 仍然共享 TCP 的有序字节流,所以存在 TCP 层队头阻塞。HTTP/3 将底层换成 QUIC,在一条 QUIC 连接中使用相对独立的 QUIC Stream,并使用 QPACK 压缩 Header,一个 Stream 丢包不会阻止其他 Stream 继续交付数据;同时还支持更快的连接建立和连接迁移。

