“Accept-Encoding: gzip, br”只是一行请求头,背后是一整套协商:服务器选哪种压缩、q 值有没有用、压缩率和 CPU 的权衡、什么数据不该压。这篇在真实服务器上试各种写法,在本机比较 gzip、brotli、zstd 的各级别,最后用 netlab 看压缩在不同带宽下省多少时间。
python.org 的 CSS(文本):
Accept-Encoding: identity → Content-Encoding = None 传输 349113 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
Accept-Encoding: gzip → Content-Encoding = gzip 传输 125147 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
Accept-Encoding: br → Content-Encoding = None 传输 349113 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
Accept-Encoding: zstd → Content-Encoding = None 传输 349113 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
Accept-Encoding: deflate → Content-Encoding = None 传输 349113 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
Accept-Encoding: gzip, br, zstd → Content-Encoding = gzip 传输 125147 字节,解压后 349113 字节,与 identity 内容一致;Vary = Accept-Encoding
example.com 首页(HTML):
Accept-Encoding: identity → Content-Encoding = None 传输 577 字节,解压后 577 字节,与 identity 内容一致;Vary = None
Accept-Encoding: gzip → Content-Encoding = gzip 传输 400 字节,解压后 577 字节,与 identity 内容一致;Vary = None
Accept-Encoding: br → Content-Encoding = br 传输 334 字节,解压后 577 字节,与 identity 内容一致;Vary = None
Accept-Encoding: zstd → Content-Encoding = None 传输 577 字节,解压后 577 字节,与 identity 内容一致;Vary = None
Accept-Encoding: deflate → Content-Encoding = None 传输 577 字节,解压后 577 字节,与 identity 内容一致;Vary = None
Accept-Encoding: gzip, br, zstd → Content-Encoding = br 传输 334 字节,解压后 577 字节,与 identity 内容一致;Vary = None
python.org 首页 /:发 Accept-Encoding: identity,响应的 Content-Encoding = gzip,11161 字节(服务器没有听从 identity,依旧发 gzip;它的 Vary 只写了 Cookie)服务器按请求头选择:python.org 的 CSS 只认 gzip;example.com 偏爱 br;q=0 的“拒绝”被无视。
Accept-Encoding: gzip;q=0.5, br;q=1 → 200,Content-Encoding = br ,334 字节 Accept-Encoding: br;q=0.1, gzip;q=0.9 → 200,Content-Encoding = br ,334 字节 Accept-Encoding: br;q=0, gzip → 200,Content-Encoding = br ,334 字节 Accept-Encoding: gzip;q=0, br;q=0 → 200,Content-Encoding = br ,334 字节 Accept-Encoding: * → 200,Content-Encoding = None ,577 字节 Accept-Encoding: *;q=0 → 200,Content-Encoding = None ,577 字节 Accept-Encoding: identity;q=0 → 200,Content-Encoding = None ,577 字节 Accept-Encoding: compress → 200,Content-Encoding = None ,577 字节 Accept-Encoding: GZIP → 200,Content-Encoding = gzip ,400 字节
example.com 不按 q 值排序,q=0 的编码仍被选中;编码名不区分大小写。
算法 / 级别 压缩后字节 比例 压缩耗时 解压耗时
gzip -1 142494 40.8% 5.6 ms 0.8 ms
gzip -6(常用默认) 125147 35.8% 10.7 ms 0.5 ms
gzip -9 124789 35.7% 12.3 ms 0.4 ms
brotli q1 131508 37.7% 1.2 ms 0.8 ms
brotli q5 109810 31.5% 5.4 ms 0.7 ms
brotli q11(最高) 98381 28.2% 431.6 ms 1.0 ms
zstd -1 129255 37.0% 0.7 ms 0.3 ms
zstd -3(默认) 119137 34.1% 1.0 ms 0.3 ms
zstd -19 106191 30.4% 57.4 ms 0.3 ms
原始 349113 字节;真实服务器的 gzip 版本是 125147 字节(第一节),相当于本机的哪个级别附近:gzip -6(常用默认)gzip -6 与真实服务器同大;brotli q5、zstd -3 又小又快;brotli q11 小但慢 77 倍。
CSS 文本 原始 349113 字节 → gzip 125147( 35.8%),brotli q5 109810( 31.5%) PNG 图片(已压缩格式) 原始 15770 字节 → gzip 15496( 98.3%),brotli q5 15630( 99.1%) 随机字节 原始 100000 字节 → gzip 100053(100.1%),brotli q5 100005(100.0%) 已经 gzip 过的 CSS 原始 125147 字节 → gzip 124913( 99.8%),brotli q5 125152(100.0%)
PNG、随机字节、已 gzip 的内容再压只有 98%~100%,甚至变大。
带宽 原样(raw) gzip -6 brotli q5 (每格:下载 ms + 解压 ms = 总 ms)
1 Mbit/s 2889 + 0.0 = 2889 1093 + 0.7 = 1094 969 + 0.9 = 970
10 Mbit/s 366 + 0.0 = 366 185 + 0.7 = 186 173 + 1.3 = 174
1000 Mbit/s 122 + 0.0 = 122 82 + 0.6 = 83 82 + 0.9 = 83慢链路上压缩收益巨大,不限速时也快 32%;解压只要 1 ms 左右。