开启HTTP/2之后,很多同学的关注点集中在多路复用和流优先级上,头部压缩这块往往一扫而过。实际上HTTP/2的HPACK压缩是整个协议提升效率的关键一环,尤其是动态表的大小设置,直接决定了重复头部的压缩效果和服务器内存开销。Nginx从1.9.5开始支持HTTP/2,其中http2_hpack_table_size指令就是用来控制这个动态表上限的,这篇文章就把它彻底讲清楚。

HPACK压缩到底是怎么工作的
HTTP/2之前的HTTP/1.1每次请求都要完整发送所有头部,Cookie、User-Agent这些又长又重复的内容在浏览器并发请求时造成了大量冗余。HPACK的思路是把头部做成键值对的索引体系,分三块:静态表、动态表和哈夫曼编码。
静态表是协议内置的61个常见头部,比如索引2代表:method: GET,索引52代表content-encoding: gzip。这些内容双方都事先知道,传输时只需要一个字节的索引号即可。动态表则是一个先进先出的缓冲区,连接双方各自维护一份,把本次会话中出现过的头部存进去,下次再遇到相同的头部同样只传索引。哈夫曼编码作为补充,对无法索引的字符串值做压缩。
动态表的大小不是无限的,它的容量以字节为单位,默认上限是4096字节(协议在SETTINGS帧中的SETTINGS_HEADER_TABLE_SIZE)。当新条目入表导致总大小超过上限时,最老的条目会被挤出。这就产生了一个权衡:表越大,能缓存的头部越多,压缩率越高;表越小,内存占用越少,但很多头部会频繁被驱逐,压缩效果退化。
http2_hpack_table_size指令的用法与含义
Nginx通过http2_hpack_table_size指令告诉客户端:本服务器能接受的动态表最大是多少字节。注意它的语义是“上限声明”,客户端真正用多大的表,取决于客户端自己的实现和协商结果。指令只能写在http或server块中,语法如下:
http {
# 默认值就是 4096
http2_hpack_table_size 8192;
server {
listen 443 ssl http2;
server_name example.ipipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# 也可以在 server 级别单独设置
# http2_hpack_table_size 16384;
}
}配置完成后用nginx -t验证语法,再nginx -s reload平滑加载。可以通过curl或者浏览器的开发者工具查看响应是否走HTTP/2,再配合抓包工具观察SETTINGS帧中的表大小通知是否生效。
需要特别说明一个常见误区:把http2_hpack_table_size调大,服务器的压缩率未必立刻提升。因为它是双向协商的,浏览器(比如Chrome默认使用的动态表大约在4096字节级别)如果自身不用更大的表,服务器单方面放大上限没有意义。这个指令更多影响的是服务端作为解码方时能接受多大的表,以及某些客户端在收到更大的上限通知后是否会扩大自己的编码表。
表大小取值的权衡与调优建议
先看压缩收益。假设一个页面加载时浏览器发出几十个请求,每个请求携带约800字节的原始头部(Cookie很长时更多),动态表命中后每个头部可能只剩几十字节甚至几个字节。表越大,长Cookie、业务自定义头这类大条目越不容易被挤出,命中率越高。对于API网关、微服务网关这种头部繁多、长连接复用频繁的场景,适当调大是有意义的。
再看内存成本。动态表是按连接维护的,每个HTTP/2连接都要独立占用一份。默认4096字节看起来不大,但如果是高并发的接入层Nginx,几万条活跃连接乘以表大小,内存增量就不能忽视。把表调到64KB,同样的连接规模下仅动态表一项就可能多消耗数百MB内存,还可能影响CPU缓存局部性,得不偿失。
实际调优可以遵循几条原则:第一,绝大多数网站保持默认4096即可,浏览器端本来就用不大的表,改了也白改;第二,如果服务的主要客户端是自家App或内部服务,双方都基于支持大表的HTTP/2库(如nghttp2、hyper),并且头部确实又多又重复,可以尝试8192或16384,同时压测观察内存和带宽变化;第三,内存紧张的边缘节点、嵌入式网关不要盲目调大,甚至可以考虑缩小来省内存;第四,每次调整后用h2load这类工具做对比测试,拿到真实的带宽节省数据再决定去留。
# 使用 h2load 做对比测试 h2load -n 1000 -c 10 -m 10 https://example.ipipp.com/ # 观察内存占用(对比调整前后) ps -o rss= -p $(pgrep -d, nginx | head -1)
总结一下,http2_hpack_table_size是一个典型的“看似简单、背后有协商机制”的指令。理解HPACK的动态表驱逐机制和SETTINGS帧的协商过程,才能判断自己的业务是否真的需要动它。对于面向普通浏览器的站点,默认值就是最稳的选择;对于可控客户端、头部重复度高的内部链路,适当放大并配合压测验证,才能把HTTP/2的压缩红利真正吃干净。