前端与 API 如何选择JSON最佳压缩算法?全面解析 Gzip、Brotli、Zstd

JsonTool
订阅
充电
|

适用对象:前后端工程师、架构/性能负责人
适用场景:HTTP/1.1/2/3 上的 JSON 响应(API、SSR 首屏数据、静态 JSON 文件)

先说结论

  • 前端浏览器
  • 服务端/API(非浏览器客户端)
  • 静态 JSON:可预压缩并直出(*_static on),**Brotli 最高等级(~11)**通常体积最小;zstd 高等级压缩更快、接近 br 的体积。(Paul Calvano)
  • 动态 JSON:控制压缩等级以避免 TTFB 上升(常用:br 5–6 / zstd 5–8 / gzip 5–6),并设置最小阈值(几百字节以上再压)。(nginx.org, GitHub)
  • 一定要做对的几件事
    • Accept-Encoding 协商,发送 Content-Encoding,并设置 Vary: Accept-Encoding(利于缓存与回退)。(MDN Web Docs)
    • CDN/反代要把 Accept-Encoding 加入缓存键,否则会错配。(AWS 文档)
    • 避免对极小/已压缩内容二次压缩;谨慎处理含敏感信息且可被反射的响应(BREACH)。(Microsoft Learn)

背景与机制:HTTP 内容编码如何工作

客户端通过 Accept-Encoding 广告支持的算法(如 gzip, br, zstd),服务器选择其一压缩并用 Content-Encoding 告知结果;对缓存体系,应以 Vary: Accept-Encoding 区分不同编码的副本。(MDN Web Docs)

相关标准与标识:

  • Gzip/Deflate:历史最久,几乎全栈默认可用。
  • Brotli:br 内容编码,RFC 7932。(rfc-editor.org)
  • Zstandard:zstd 内容编码,RFC 8878(注册了媒体类型与编码名)。(datatracker.ietf.org)

浏览器与生态支持(2025-08)

  • Brotli(br):已被 Chrome/Firefox/Edge/Safari 长期支持,生产级首选。(维基百科)
  • Zstandard(zstd)
  • cURL/libcurl:已支持 br/zstd/gzip/deflate,便于压测与巡检。(everything.curl.dev)

小贴士:看到 Safari 请求头只有 gzip, deflate, br 时,服务端不可回 zstd,否则页面/资源会“空白/损坏”。业界曾出现代理/平台误回 zstd 导致 Safari 打不开页面的案例。(community.cloudflare.com, Answer Overflow)


压缩效果与 CPU/延迟权衡

来自大规模实测与行业测评的共识(不同内容会有差异,请以自家样本验证):

  • 动态内容zstd(~L12)≈ br(~L5) 的体积,但压缩时间常 更短,对 TTFB 更友好。(Paul Calvano)
  • 静态内容:可预压到高等级,Brotli 11 常比 zstd 19 更小,但 zstd 在高等级的压缩耗时显著更低。(Paul Calvano)
  • 总体趋势zstd 解压速度明显占优;br 体积更小但高等级压缩耗时大;gzip 兼容性最好、比特率一般。(The Cloudflare Blog)

Paul Calvano 的公开实测文章与工具提供了可复核的对比与时间/体积表格,建议把自家页面/JSON 样本扔进去评估合适的等级梯度。(Paul Calvano)

生产落地:Nginx上的典型配置

目标:同时支持 zstd / br / gzip,按客户端能力协商,开启静态预压直出,并对 JSON 等文本类型生效。

1)Gzip(基础盘)

1
2
3
4
5
gzip on;
gzip_comp_level 6;
gzip_min_length 512;
gzip_types application/json application/javascript text/plain text/css application/xml;
gzip_vary on; # 让缓存体系感知编码差异

(指令详见官方模块文档。)(nginx.org)

2)Brotli(需模块 ngx_brotli

1
2
3
4
5
6
7
8
9
# 动态模块示例(放在 http {} 之前的全局位置)
# load_module modules/ngx_http_brotli_filter_module.so;
# load_module modules/ngx_http_brotli_static_module.so;

brotli on;
brotli_comp_level 5; # 动态建议 4–6;静态可到 11
brotli_min_length 512;
brotli_types application/json application/javascript text/plain text/css application/xml;
brotli_static on; # 若存在 .br 预压文件则直出

(指令/默认值见模块 README 与 NGINX 文档。)(GitHub, docs.nginx.com)

3)Zstandard(需模块 zstd-nginx-module

1
2
3
4
5
6
7
8
9
# load_module modules/ngx_http_zstd_filter_module.so;
# load_module modules/ngx_http_zstd_static_module.so;

zstd on;
zstd_comp_level 6; # 动态常用 5–8;静态可更高
zstd_min_length 512;
zstd_types application/json application/javascript text/plain text/css application/xml;
zstd_static on; # 若存在 .zst 预压文件则直出
# zstd_static always; # ⚠️ 不建议:会无视客户端是否支持 zstd

(指令/用法见官方 README。)(GitHub)

验证要点

  • Safari:应返回 br/gzip,而非 zstd。
  • 代理/CDN:缓存键需包含 Accept-Encoding,并允许从源站获取压缩版本或在边缘压缩。(AWS 文档)

API 与前端的细节坑点

  1. 回退与协商
  • 永远尊重 Accept-Encoding;不要用“强制发送压缩文件(*_static always)”误伤不支持的客户端。(GitHub)
  1. 小文件/阈值
  • 对几十/几百字节的 JSON,压缩反而可能增大/增加 CPU;设置 \*_min_length(如 ≥ 256/512B)。(GitHub)
  1. 动态内容的 TTFB
  • Brotli 高等级压缩会显著推高 TTFB;动态 JSON 建议 中档等级(br 5–6 / zstd 5–8 / gzip 5–6),把更高等级留给静态预压。(Paul Calvano)
  1. 安全(BREACH/CRIME 家族)
  • 包含机密信息能反射用户输入的响应(例如含 token 的 HTML+回显参数),启用压缩存在泄露风险;ASP.NET、等框架默认在 HTTPS 下禁用/谨慎启用。API 纯 JSON 一般风险较低,但仍应评估。(Microsoft Learn)
  1. 窗口/流式与刷写
  • zstd HTTP 建议窗口 ≤ 8MB(兼容性/内存占用);长连接/流式(SSE/NDJSON)要注意 flush 策略,避免编码器过度缓冲拖慢首包。(greenbytes.de)
  1. 字典压缩(高级)
  • 自控客户端(App/IoT/内网)可用 zstd 字典进一步减小强结构化 JSON;Web 端有“Compression Dictionary Transport”在推进(Use-As-Dictionary/Dictionary-ID/ dcz/dcb),但仍属实验特性,谨慎上生产。(Chrome for Developers, MDN Web Docs)

实验:用 JSON 跑分

目的:得到数据驱动的等级选择

A. 本地离线压测(静态样本)

1
2
3
4
5
6
7
8
9
10
11
12
# 准备测试样本
curl -s https://jsonplaceholder.typicode.com/posts > sample.json

# 压缩
gzip -c -6 sample.json > sample.json.gz
brotli -f -q 5 sample.json -o sample.json.br
zstd -f -q -19 -T0 sample.json -o sample.json.zst # 或 -5/-8 比较

# 统计体积
wc -c sample.json*
# 解压耗时(多次取中位)
/usr/bin/time -f '%E %C' zstd -d -c sample.json.zst > /dev/null

对不同 等级(-q/-#、不同 样本(小/大/重复字段多) 多跑几轮,记录体积与耗时,得到“体积—时间” Pareto 前沿。

若用 Node.jszlib.brotliCompress/zlib.gzip 可直接测;zstd 需三方库或 CLI。(MDN Web Docs)

B. 线上协商与回退验证

1
2
3
4
5
6
7
8
# 期望 zstd(Chromium 系列)
curl -sI -H 'Accept-Encoding: gzip, deflate, br, zstd' https://api.yoursite.com/data

# 期望 br(Safari)
curl -sI -H 'Accept-Encoding: gzip, deflate, br' https://api.yoursite.com/data

# 自动解压验证(libcurl)
curl --compressed -I https://api.yoursite.com/data

确认响应头 Content-Encoding 与预期一致;同时检查是否返回 Vary: Accept-Encoding。(everything.curl.dev)

选型与参数建议

场景 推荐编码 动态等级 静态(预压)
Web 首屏/接口(含 Safari) br(回退 gzip) br 5–6 / gzip 5–6 br 9–11
Web 首屏/接口(不含 Safari 的私域/内网) zstd(回退 br/gzip) zstd 5–8 zstd 15–19(或 br 11 更小)
App/IoT/内网 API zstd zstd 5–8 zstd 15–19
超小 JSON(< ~512B) 可能不压缩 —— ——

真正的最优点依赖你们的 JSON 结构、并发/CPU、水位与 TTFB 目标,务必结合上文实验跑数据。参考业界测评和大规模对比,zstd 常在“同等体积下更快”或“同等时间下更小”之间取得较优平衡,而 br 在极限体积上仍占优势。(Paul Calvano, The Cloudflare Blog)

附:常用 Nginx/模块/标准资料

  • Nginx gzip 模块(官方)(nginx.org)
  • ngx_brotli 模块 README(Google)(GitHub)
  • zstd-nginx-module README(tokers)(GitHub)
  • MDN:Accept-Encoding / Content-Encoding(协商/语义)(MDN Web Docs)
  • RFC 7932(Brotli)/ RFC 8878(Zstandard)(格式与 HTTP 编码注册)(rfc-editor.org, datatracker.ietf.org)
  • zstd HTTP 窗口限制草案(兼容性/内存)(greenbytes.de)
  • CloudFront:压缩与缓存键(实践参考)(AWS 文档)
  • BREACH/压缩风险(.NET 文档与安全资料)(Microsoft Learn)
  • Paul Calvano:gzip/br/zstd 选择与在线测试器(性能与体积对比)(Paul Calvano)