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. 核心对比

对比项HTTP/1.1HTTP/2HTTP/3
底层传输TCPTCPQUIC(基于 UDP)
同一个源的常见连接方式多条 TCP 连接通常一条 TCP 连接通常一条 QUIC 连接
并发方式依靠多条连接一条连接中的多个 HTTP/2 Stream一条连接中的多个 QUIC Stream
消息传输起始行和 Header 使用文本格式二进制分帧基于 QUIC Stream 的二进制帧
Header 压缩不支持协议级压缩HPACKQPACK
队头阻塞有应用层和 TCP 层阻塞解决应用层阻塞,仍有 TCP 层阻塞没有 TCP 式跨流阻塞,单个 Stream 内仍需有序交付
TLSHTTP 可不使用,HTTPS 需要 TLS协议不强制,但浏览器通常使用 HTTPSQUIC 集成 TLS 1.3 握手
网络切换通常需要重新连接通常需要重新连接支持连接迁移

这里的连接数量都是常见实现,不是协议强制规定的固定数量。不同源、连接替换、代理和浏览器隔离策略都可能产生额外连接。

4. HTTP/1.1:通过多条连接实现并发

HTTP/1.1 默认支持持久连接,一条 TCP 连接可以被多个请求重复使用,并不需要每次请求都重新建立连接。

但是,HTTP/1.1 没有 Stream ID 来区分同一连接中的多个并发响应。它虽然支持请求管线化,可以连续发送多个请求,但服务器必须按照请求顺序返回响应,前面的响应较慢时,后面的响应仍然需要等待。因此浏览器实际通常不会依赖管线化,而是对同一个源建立多条 TCP 连接来实现并发:

同一个源
├── TCP 连接 1:请求 A → 响应 A → 请求 D
├── TCP 连接 2:请求 B → 响应 B → 请求 E
└── TCP 连接 3:请求 C → 响应 C → 请求 F

HTTP/1.1 协议没有规定同一个源必须建立多少条连接。面试中经常提到的“浏览器同域名最多 6 条连接”只是常见实现策略,不是协议限制,具体数量可能因浏览器和运行环境而变化。

这种并发方式存在几个问题:

  • 能同时处理的请求数量受连接数限制,超出的请求需要排队。
  • 慢请求会长期占用一条连接。
  • 每条 TCP 连接都需要独立建立,并分别进行拥塞控制;HTTPS 连接还要完成 TLS 握手。
  • 每个请求都会重复携带 Cookie、User-Agent 等 Header。

5. HTTP/2:一条 TCP 连接并发传输多个请求

HTTP/2 引入二进制分帧,把请求和响应拆成 HEADERS、DATA 等帧。每个请求和响应属于一个带有 Stream ID 的 HTTP/2 Stream,不同 Stream 的帧可以在一条 TCP 连接中交错传输:

一条 TCP 连接
├── Stream 1:HTML
├── Stream 3:CSS
├── Stream 5:JavaScript
└── Stream 7:图片

HTTP/2 的主要改进

  1. 多路复用

    多路复用可以直接理解为:一条连接同时承载多个 Stream。每个请求和对应响应使用一个独立的逻辑 Stream,但不需要为每个 Stream 重新建立 TCP 连接或进行 TLS 握手。

    不同 Stream 的数据会被拆成带有 Stream ID 的帧,然后在同一条连接中交错传输:

    Stream 1 HEADERS
    Stream 3 HEADERS
    Stream 1 DATA
    Stream 5 HEADERS
    Stream 3 DATA
    Stream 5 DATA

    接收方根据 Stream ID 把帧归类到对应的请求。这样多个请求可以同时发出,不需要等待某条连接空闲;某个请求的服务端处理较慢时,连接仍然可以传输其他 Stream 的数据。

  2. 二进制分帧

    HTTP 消息被拆成可识别所属 Stream 的帧,使不同请求的数据能够在同一连接中交错传输。

  3. 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
├── QPACK 编码、解码 Stream
├── 双向 Stream:HTML 请求和响应
├── 双向 Stream:CSS 请求和响应
└── 双向 Stream:JavaScript 请求和响应

QUIC Stream 是什么?

QUIC Stream 是一条 QUIC 连接内部的逻辑数据通道,不是一条独立网络连接。创建 Stream 不需要重新进行网络连接或 TLS 握手。

它具有以下特点:

  • 同一个 Stream 内的数据可靠并且按顺序交付。
  • 如果一个 Stream 中间的数据丢失,这个 Stream 需要等待重传。
  • 不同 Stream 的交付相对独立,一个 Stream 等待重传时,其他 Stream 可以继续交付数据。
  • 所有 Stream 仍然共享连接的总带宽和拥塞控制;发生丢包后,整个连接的发送速度仍可能下降。

HTTP/3 的一个请求和对应响应通常使用一个客户端创建的双向 QUIC Stream。控制信息和 QPACK 状态则使用单向 Stream。

HTTP/3 的主要改进

  1. 减少跨 Stream 的传输层队头阻塞

    某个 Stream 丢包时,主要由该 Stream 等待重传,不会像 TCP 那样要求其他 Stream 等待缺失的字节补齐后才能继续交付。

  2. QPACK Header 压缩

    HTTP/3 使用适配 QUIC 多 Stream 模型的 QPACK。它的目标与 HPACK 相同,都是减少重复 Header,但处理动态表和到达顺序的方式不同。

  3. 更快的连接建立

    QUIC 将传输连接和 TLS 1.3 握手结合起来,首次连接通常可以用 1-RTT 建立。只有客户端和服务器之间存在之前连接保存的会话信息,并且服务器允许时,才能尝试使用 0-RTT 提前发送数据。

    0-RTT 数据存在重放风险,因此并不是所有请求都适合使用,也不能把 HTTP/3 简单理解为“每次都是 0-RTT”。

  4. 连接迁移

    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 看到的是一条统一编号、必须按顺序交付的字节流:

数据包 1:Stream 1 的数据,丢失
数据包 2:Stream 3 的数据,已到达
数据包 3:Stream 5 的数据,已到达

即使数据包 2 和数据包 3 已经到达,TCP 也要等待数据包 1 重传后,才能继续按顺序向 HTTP/2 交付数据。因此 Stream 1 的丢包可能让 Stream 3 和 Stream 5 一起等待。

QUIC 则在传输层直接管理多个 Stream,并分别维护每个 Stream 的数据顺序:

Stream 1:等待丢失数据重传
Stream 3:继续交付已经到达的数据
Stream 5:继续交付已经到达的数据

所以 HTTP/3 避免的是不同 Stream 之间因为统一传输顺序产生的阻塞,并不是完全没有阻塞:

  • 同一个 Stream 内仍然需要按顺序交付,丢失中间数据时,后续数据仍要等待。
  • 所有 Stream 仍然共享连接带宽和拥塞控制,丢包可能让整个连接的发送速度下降。

可以总结为:

HTTP/1.1:请求之间可能互相等待
HTTP/2:请求可以并发,但 TCP 丢包可能让所有 Stream 等待
HTTP/3:不同 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 继续交付数据;同时还支持更快的连接建立和连接迁移。

参考资料