在配置网站路由或CDN节点时,开启gzip压缩几乎是标配操作。它能显著减少传输体积,加快页面加载速度。但很多人没意识到,gzip压缩也会悄悄改变缓存系统的运作方式,进而影响缓存命中率。
同一个资源,可能变成多个缓存对象
举个例子,服务器上有一个JS文件叫app.js,大小为300KB。如果未开启gzip,所有用户请求这个文件时,缓存服务器(比如CDN节点)会统一返回同一个缓存副本。但一旦启用gzip,情况就变了:支持gzip的浏览器会带上Accept-Encoding: gzip头,而不支持的老设备可能不会带。
这样一来,缓存系统就得判断——这个请求要不要压缩版本?结果就是,app.js被缓存成了两个独立的对象:一个是原始未压缩版,另一个是gzip后的版本。它们共享同一个URL,但在缓存层里是两份数据。
缓存键的变化是关键
大多数现代缓存系统(如Varnish、Nginx Proxy Cache、Cloudflare等)在生成缓存键时,默认不包含Accept-Encoding。这意味着不同编码请求可能命中同一个缓存项,造成混乱。更合理的做法是把客户端支持的压缩方式纳入缓存键的一部分。
以Nginx为例,可以通过添加变量来区分:
gzip on;
gzip_types text/plain text/css application/json application/javascript;
proxy_cache_key "$scheme$request_method$host$request_uri$gzip_accept_encoding";
这样,当用户请求带有Accept-Encoding: gzip时,缓存键会自动包含这一信息,避免未压缩和压缩版本互相覆盖。
移动网络下的实际表现
在4G/5G环境下,很多中低端安卓机仍使用老旧浏览器,不一定完整支持gzip解压。如果你的站点主要面向这类用户,盲目开启全量gzip反而可能导致缓存碎片化——大量小众请求无法复用已有缓存,命中率下降。
一个折中方案是按用户代理或设备类型动态控制压缩:
if ($http_user_agent ~* "(Mobile|Android)") {
gzip off;
}
当然,这种做法需要权衡流量成本和缓存效率。更好的方式是结合日志分析,看哪些客户端占比高,再决定是否对特定群体关闭压缩。
HTTPS与协商缓存的叠加效应
现在大部分站点走HTTPS,而TLS握手本身有一定开销。如果因为gzip导致缓存未命中,就得反复回源拉取资源,不仅增加延迟,还加重源站负担。尤其是API接口类内容,频繁的小数据包即使启用了Brotli或gzip,也未必省多少流量,反而让ETag和Last-Modified这些协商机制失效得更频繁。
建议对JSON、XML这类文本接口明确设置缓存策略,并确保压缩状态一致:
location ~ \.(json|xml)$ {
add_header Cache-Control "public, max-age=3600";
gzip on;
proxy_cache_valid 200 1h;
}
保持压缩开关稳定,能让CDN节点更有效地复用缓存,提升整体命中率。