← 中文资源

简体中文版本

HTTP — 双重压缩破坏响应或白白浪费

诊断 Content-Encoding 被应用两次的问题:下游乱码或解压失败、压缩已压缩字节浪费 CPU——修管线顺序。

阅读英文原文

症状

  响应头直接出卖了管线:Content-Encoding: gzip, gzip 两个值堆叠即两层各压了一次。客户端症状按严格程度分裂:宽容的解码器解一层显示乱码、严格的直接拒绝。反向的浪费体现在 CPU 曲线上:代理对 JPEG 与 MP4 流量做压缩,烧掉周期换回负收益——熵编码过的媒体压不动了。宽松客户端能用而严格客户端报解码错误,是编码栈 bug 而非数据损坏的分界线。

诊断思路

  先带 Accept-Encoding 打 HEAD 看最终头:单一 Content-Encoding 列出两个算法是双重压缩凭证,缺 Vary: Accept-Encoding 则预示下一站的缓存变体污染;再用 curl –compressed 严格解码验证可解性;最后检查各层压缩配置,应用框架、代理、CDN 三层都开着且 MIME 类型重叠就是结构性成因。

根因

  Content-Encoding 应由最贴近线路的一层恰好应用一次。两层各自压缩时头部堆叠、合规客户端以错误顺序解码或失败;某层压缩已压缩的媒体时,CPU 成本照付而体积收益为零。

修复与验证

  压缩收敛到恰好一层(通常选最外层代理统一策略):nginx 开 gzip、类型限定 json/js/css、绝不包含 image/jpeg video/mp4 application/zip;内层(应用框架中间件)关闭压缩。中间件必须检查响应体时精确解压一次并诚实地重设头部。每个缓存层保留 Vary: Accept-Encoding——丢了它,压缩变体会被发给请求 identity 的客户端,那是另一种腐坏。验证时响应恰好一个 Content-Encoding(媒体类为零)、严格客户端干净解码、媒体流量的代理 CPU 回到基线。用剥掉 Accept-Encoding 来“关闭”压缩会破坏缓存变体选择并禁用合法的客户端解码——配置类型而不是删头。

代码示例

$ curl -sI -H "Accept-Encoding: gzip, br" https://app.example.com/api/data
HTTP/1.1 200
Content-Encoding: gzip, gzip     <- applied twice
Vary: Accept-Encoding
# nginx (the only compressor)
gzip on;
gzip_comp_level 5;
gzip_types application/json application/javascript text/css;
# NOT image/jpeg video/mp4 application/zip — already compressed
if (res.getHeader('content-encoding') === 'gzip') {
  const body = await gunzip(raw);
  inspect(body);
  res.removeHeader('content-encoding');  // pass along uncompressed
}