HTML、CSS、JavaScript 这类文本文件有个特点:重复字符多、可预测性高,压缩率通常能到 60% 以上。也就是说,一个 100KB 的 HTML 页面,压缩后可能只有 20 到 30KB。如果服务器没有开启压缩,这部分体积就原样发给了访客和蜘蛛,属于白白浪费的等待时间。
这件事值得单独拿出来查一次,因为它成本极低、影响面却覆盖全站每一个页面。
先确认:到底压了没有
别只看配置文件,要看真实响应。最简单的做法是用命令行工具请求两次:一次带上 Accept-Encoding: gzip 请求头,一次不带,然后对比两次响应的 Content-Encoding 和 Content-Length。
- 带压缩请求头时,响应里应该出现 Content-Encoding: gzip 或 Content-Encoding: br。
- 不带时,体积应明显更大;如果两者体积完全一样,基本说明压缩没生效。
- 浏览器开发者工具的 Network 面板里,Size 一列通常显示传输体积,Content 一列显示解压后体积,差异就是压缩收益。
抽查对象不要只挑首页,至少覆盖:首页、一个栏目列表页、一篇内容页、一个 CSS 文件、一个 JS 文件、站点地图文件。
哪些类型该压,哪些别压
大体原则是:文本类压缩收益高,二进制已压缩格式压了也白压。
- 建议开启:text/html、text/css、application/javascript、application/json、image/svg+xml、XML(含站点地图和 RSS)。
- 不必开启:JPEG、PNG、WebP、AVIF、MP4、WebM、WOFF2、ZIP 等。这些格式本身已经过压缩,再走一遍 gzip 只是消耗 CPU,体积几乎不会变小。
动态压缩与预压缩的选择
动态压缩是每次请求时现场压一遍,配置简单但吃 CPU;预压缩则是提前把 .gz 或 .br 文件生成好放在同目录,请求时直接发送,几乎零开销。流量较大的站点,建议静态资源走预压缩,动态页面再考虑动态压缩。
无论用哪种方式,运营上要留意一件事:内容更新后,旧的预压缩文件有没有跟着重新生成。改了 CSS 却还在发旧的压缩文件,前端表现就会对不上。
别漏掉 Vary 响应头
如果 CDN 或反向代理是按 URL 缓存,却没有区分压缩与非压缩版本,就可能把压缩版发给不支持的客户端,或者把未压缩版发给所有客户端。加上 Vary: Accept-Encoding,或者干脆让 CDN 统一负责压缩、源站关闭压缩,都是可行做法,但不要两层同时压。
压缩级别的取舍
级别不是越高越好。gzip 一般设在 5 到 6 已经接近收益上限,Brotli 质量设 4 到 6 性价比通常较好。再往上调,体积减少有限,CPU 占用却明显上升,高峰期容易变成瓶颈。
一份可照着走的自查清单
- 抽查各类资源的响应头,确认有 Content-Encoding。
- 确认源站与 CDN 没有重复压缩,避免二次压。
- 确认响应头里有 Vary: Accept-Encoding,缓存不会串味。
- 确认缓存策略对压缩版本和非压缩版本的处理一致。
- 确认静态资源有预压缩文件,且内容更新后会重新生成。
- 确认极小文件(1KB 以下)不必强压,收益有限。
- 换服务器、换 CDN、调整缓存规则后,重新验证一遍。
几个常见误区
- 以为配置文件里写了 gzip on 就万事大吉,没看真实响应。
- 只压了 HTML,漏掉 CSS、JS、JSON 和站点地图。
- 对图片再压一次,徒增 CPU 开销。
- 在源站开了压缩,CDN 又压一遍,结果头部信息混乱。
传输压缩算不上什么高级技巧,更像一次性的基础设施检查。花十几分钟确认一遍,把这几个检查点写进运维清单,之后每次环境变更时复测一次就够了。