资讯文章 资讯 · NEWS
gzip、Brotli、zstd 该开哪个:目录站的压缩协商和 CDN 那一层
2026-10-24 约 2,254 字 预计阅读 6 分钟 yiyunkj
g
HTML、CSS、JS 这些文本资源开压缩,收益一般在 60% 到 80%,是投入产出比最高的一类优化。但它的配置有几个容易踩的点,配错了不但没收益,还会增加 CPU 和延迟。

图片来源:网络,版权归原作者所有,侵权请联系删除
三种算法各自的位置
| 算法 | 压缩率 | 压缩速度 | 支持情况 | 该用在哪 |
|---|---|---|---|---|
| gzip | 基准 | 快 | 全部浏览器 | 最后的兜底 |
| Brotli | 比 gzip 小 15–30% | 高等级很慢 | 主流浏览器基本都支持 | 静态资源预压缩 |
| zstd | 接近 Brotli | 明显更快 | Chrome/Edge 123+、Firefox 126+,Safari 暂缺 | 动态响应、高负载 |
选择其实不复杂:静态资源用 Brotli 预压缩,动态响应用 zstd 或低等级 Brotli,gzip 留着兜底。
Brotli 的高等级(比如 11 级)压缩时间很长,不适合对每个请求实时压缩。正确做法是构建时把 CSS、JS 预压成 .br 文件,运行时直接发文件,零压缩成本。动态响应用高等级 Brotli 会把 CPU 吃光,这时候要么降到中低等级,要么换 zstd。
协商是怎么发生的
浏览器发 Accept-Encoding 头声明自己支持什么,服务器挑一个返回,并在 Content-Encoding 里说明用了哪种。顺序由客户端声明决定,服务器按自己的优先级挑。
# 请求
Accept-Encoding: gzip, deflate, br, zstd
# 响应
Content-Encoding: br
Vary: Accept-Encoding
两个硬性要求:Content-Encoding 必须真实反映压缩方式,说压了没压或者压了不说,浏览器都会出错;Vary: Accept-Encoding 必须加,否则中间的缓存会把 Brotli 版本发给不支持 Brotli 的客户端。
Vary 这一条在 CDN 场景下尤其重要。CDN 按 URL 加 Vary 的组合做缓存键,少了 Vary 就会缓存串味。
CDN 那一层容易重复劳动
源站压了一遍,CDN 又压一遍。第二遍通常压不出多少,白白消耗边缘节点的 CPU。
判断方法:看源站响应头里有没有 Content-Encoding,有的话 CDN 侧就该关掉自动压缩,直接透传。各家 CDN 的配置项名字不同,但思路一样——让压缩只发生一次。
反过来也有坑:源站不压、指望 CDN 压。这种情况下回源流量没省下来,而且回源慢的问题还在。能让源站压就压了,缓存时间长的静态资源尤其应该预压缩成文件。
哪些不该压
已经压过的格式不要二次压缩。JPEG、PNG、WebP、AVIF、MP4、ZIP 这些再压一遍收益不到 2%,纯浪费 CPU。
小文件也不要压。几十到一两百字节的响应,压缩后的体积可能反而变大,因为算法本身有头部开销。主流服务器都有最小压缩阈值(常见 1KB 左右),保持默认即可。
目录站上要特别留意 favicon 和缩略图。缩略图本身是 JPEG 或 WebP,不应该进压缩列表;favicon 如果是 ICO 或 PNG 同理。
zstd 要不要现在上
看场景。支持度上,Chrome 和 Edge 从 123 起支持,Firefox 从 126 起,Safari 目前还不支持,所以必须有 Brotli 或 gzip 兜底。
值得上的情况:站点以动态内容为主、页面缓存命中率低、每次请求都要重新压缩。zstd 的压缩速度优势在这种场景下能直接省 CPU,而且解压也更快。
不急着上的情况:内容以静态资源为主,且这些资源已经在 CDN 上长期缓存。这种情况下文件被缓存后根本不需要解压,Brotli 更小的体积更划算——有实测对比显示 Brotli 在静态文件上比 zstd 小约 3%,资源数量多的时候这个差距会累积。
还有个进阶用法:zstd 支持训练自定义字典。对于大量结构相似的小 JSON 响应,用自己项目的样本训练出的字典,压缩率能超过通用字典。目录站的 API 输出如果形态固定,这是个可以试的方向。
nginx 侧的最小配置
Brotli 和 zstd 都不在 nginx 核心里,需要额外模块 ngx_brotli 和 zstd 模块,编译时加进去或者用预编译镜像。
brotli on;
brotli_comp_level 6; # 动态压缩别用 11
brotli_types text/plain text/css application/json
application/javascript image/svg+xml;
gzip on;
gzip_types text/plain text/css application/json
application/javascript image/svg+xml;
gzip_min_length 1024;
# 加了压缩就必须有这一条
add_header Vary Accept-Encoding;
如果用了预压缩的 .br 文件,走 brotli_static on;,服务器检测到同名 .br 文件就直接发,不再实时压缩。
验收三步
第一步,curl 带 Accept-Encoding 直接量,看 Content-Encoding 和实际字节数:
curl -s -o /dev/null -w '%{size_download}\n' \
-H 'Accept-Encoding: br, gzip' https://example.com/
第二步,确认 Vary 头在。CDN 场景下还要清一次缓存再看,否则拿到的可能是修改前的版本。
第三步,跑一遍 PageSpeed Insights,看有没有「启用文本压缩」的提示。它会逐个资源列出未压缩的项,比自己一个个查省事。
最后提醒一句:压缩优化的是传输体积,改不了内容本身有多大。一个 500KB 的 JS 包压完还有 120KB,这时候该做的是拆包和删代码,不是换个压缩算法。