HTTP/2 中如何实现首部压缩?

HTTP/2 使用 HPACK 算法通过静态和动态表以及 Huffman 编码来压缩请求和响应头,减少数据传输量。

浏览器机制 困难 性能优化 HTTP/2 HTTP

HTTP/2 中的首部压缩通过使用 HPACK 算法实现,以高效减少每次请求和响应的头部数据大小,从而提高性能并降低带宽占用。

其实现原理包括以下主要机制:

1. 表结构的组成与维护

HTTP/2 在客户端和服务器端维护两份共享的表来存储头部信息:

  • 静态表:包含预定义的 61 个常⺡头部键值对(如 :method:pathhost),这些由协议固定设置。
  • 动态表:用于在连接生命周期内持续跟踪发送的头部数据,由双方渐进更新;任何新头部键值对会被追加或替换现有条目。

    例如,如果首次请求发送 accept-language: zh-CN,该条目即被缓存到动态表中;后续请求若使用相同值,只需发送索引号而不发送全字符串。

2. 编码和传输规则

  • 索引编码:头部数据基于表索引来表示,常见步骤如下:
    • 完整键值对存在于表中:发送单个0或1开头的标识帧及表索引。
    • 头部值匹配但名称不匹配:使用增量编码发送差异化名称部分。
    • 新增条目:添加到动态表尾部并进行编号更新。
  • Huffman编码:头部串值应用哈夫曼压缩进一步缩小体积;这使用预设的哈夫曼树,根据字符频率优化编码。

    在 HTTP/2 Frame 结构中处理时:

    Headers Frame: {
        Index (0-6 bits) : 表示键值的索引位置
        Value Prefix Flags (2-3 bits) : 控制 Huffman 标志位
        Dynamic Table Size Update : 动态更新指令
    }
    

3. 工作流程示例

假设初次请求携带头部 accept: text/html; charset=utf-8

  • 初始传输时需发全字符串并更新动态表。
  • 下次请求若值相同(accept: text/html; charset=utf-8):客户端只发送动态表索引 ##2 (索引实例)
  • 如服务端收到差异值:将索引对应项替换为新数据并响应更新指令;该表生命周期与连接保持一致。

通过此机制,在连接中重复数据的传输开销接近零;尤其对像 cookie 一类高频变更首部,压缩增益可达50%以上。