上一篇讲 HTTP/2 多路复用,这一篇讲 HTTP/2 的另一个核心改造:头部压缩(HPACK)。

HTTP/1.1 的浪费:每个请求重发一堆 header

先看 HTTP/1.1 的问题。一个页面发 30 个请求,每个请求都带一堆 header:

GET /api/user HTTP/1.1
Host: rubyfun.cn
User-Agent: Mozilla/5.0 ...    ← 每个请求都发这一长串
Accept: application/json
Cookie: sessionId=abc123; ...  ← Cookie 可能几百字节
Accept-Encoding: gzip, br
Accept-Language: zh-CN

这 30 个请求的 User-AgentAccept-EncodingAccept-Language 几乎完全相同,但每个请求都完整发一遍。这些重复的 header 加起来可能比 body 还大——纯粹的浪费。

HTTP/2 的 HPACK 就是来解决这个浪费的。

HPACK 三件套:静态表、动态表、哈夫曼

HPACK(HTTP/2 的头部压缩算法)用三个机制消除冗余:

HPACK 的三个压缩机制

第一件:静态表(Static Table)

HPACK 预定义了 61 个最常见的 header,每个有固定的索引号。比如:

  • 索引 2::method: GET
  • 索引 4::path: /
  • 索引 23:content-type: text/html

发送方不用发完整的 :method: GET,只发「索引 2」这个编号(一两个字节)就行。接收方查静态表还原。最常见的 header 几乎都在静态表里,省掉的非常可观。

第二件:动态表(Dynamic Table)

静态表覆盖通用的 header,但 CookieUser-Agent、自定义 header 这些是每个站点特有的,静态表管不了。动态表解决这个问题:

动态表的工作原理

  • 第一个请求发完整 header(如 Cookie: sessionId=abc123)。
  • 发送方和接收方都把这个 header 存进动态表,分配索引号(从 62 开始)。
  • 后续请求如果带相同的 header,只发「索引号」,不用重发完整内容。

因为动态表在连接级别共享(不是每个 stream 一个),所以 stream 1 发过的 header,stream 2、stream 3 都能引用索引。这就是「重复 header 只发一次」的原理——第一次全发,之后只发索引号。

动态表有大小限制(SETTINGS_HEADER_TABLE_SIZE),满了会淘汰旧的。Cookie 这种重复率高的 header 进动态表后,省掉的尤其多。

第三件:哈夫曼编码(Huffman Coding)

对于动态表里没有的、必须发完整值的 header,HPACK 用哈夫曼编码压缩。哈夫曼编码对高频字符用短编码、低频字符用长编码,对文本 header 能压缩 20%-30%。配合静态表和动态表,整体压缩率非常高。

HPACK 的效果

HPACK 的压缩效果是实打实的。一个典型的 HTTP/1.1 请求 header 约 700-800 字节;经过 HPACK 压缩,同样内容的 header 能压到几十字节——尤其在高重复率场景(同站点连续请求)。

这对移动网络尤其重要:移动端每个字节都金贵,header 从 800 字节降到 50 字节,不仅省带宽,还省了 TCP 慢启动阶段的发送时间(窗口小的时候少发字节 = 更快)。

安全收益:HPACK 让 HTTPS 更快

HPACK 还有个意外的安全收益。HTTPS 加密的是整个报文(包括 header)。HTTP/1.1 时代,每次都重发一堆 header,加密后体积更大。HPACK 压缩了 header,等于减少了需要加密和传输的数据量,间接提升了 HTTPS 的性能。这也是 HTTP/2 配合 HTTPS 体验更好的一个原因。

取舍与边界

这里有个延伸问题:HTTP/3 为什么不用 HPACK,要另造一个 QPACK?根子在传输层:

为什么 HTTP/3 不用 HPACK 要用 QPACK

HPACK 有几个值得注意的边界:

  • HPACK 是 HTTP/2 专属。HTTP/3 有自己的头部压缩(QPACK),因为 QUIC 的 stream 独立性让 HPACK 的动态表同步机制不适用。QPACK 在思路上一脉相承但做了适配。
  • 动态表有安全考虑。CRIME 攻击利用压缩 + 加密的信息泄露(攻击者通过观察压缩后体积变化猜测 Cookie 内容)。防御是关闭 Cookie 的压缩,或用专门的 mitigation。这是 HPACK 安全设计要考虑的点。
  • HPACK 对小请求收益小。如果请求本身就一两个 header,HPACK 的索引机制省不了多少。它的价值在请求量大、header 重复多的场景。

收束:HPACK 是 HTTP/2 的隐性提速

多路复用是 HTTP/2 最显眼的特性,HPACK 是它隐性的提速器。两者配合:多路复用让一条连接跑很多请求,HPACK 让这些请求的 header 不再重复发。没有 HPACK,HTTP/2 的多路复用收益会被 header 冗余吃掉一大半。

下一篇讲 HTTP/3——为什么连 TCP 都换了,QUIC 是怎么解决 TCP 层队头阻塞的。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 HTTP」,欢迎关注公众号 「十三Tech」。后续会按 URL 与报文、连接与传输、缓存与协商、安全与边界、HTTP/2 与 HTTP/3 这条线更新。

十三Tech公众号二维码