根本原因是缓存未识别文件变更,需聚焦三点:确认缓存是否命中(检查proxy_cache_valid、key一致性、keys_zone配置)、验证缓存键是否稳定(显式设proxy_cache_key避免$cookie/$args干扰)、排查响应头是否抑制校验(如no-cache指令、固定etag、前端请求头干扰)。

静态资源更新后用户仍看到旧内容,常见原因不是 Nginx 没缓存,而是缓存没“认出”文件已变——尤其在反向代理场景下,后端文件变了,Nginx 却还在返回旧缓存副本。排查要聚焦三点:缓存是否真命中、缓存键是否稳定、响应头是否干扰验证。
确认缓存是否实际生效且可复用
先看 Nginx 是否真的把新资源缓存进去了,还是压根没存、或存了但根本没被命中:
- 检查 proxy_cache_valid 是否覆盖当前状态码:比如后端返回 200,但配置只写了
proxy_cache_valid 502 10m,那 200 响应就不会进缓存,自然谈不上更新同步 - 用 log_format 打印实际缓存 key,例如:
log_format cache_debug '$remote_addr - "$upstream_cache_key"';
再查 access.log,确认同一 URL(如/static/app.js)是否始终生成相同 key;若含未清理的$args或$cookie,key 就会散列,导致“同个文件多个缓存副本” - 验证 proxy_cache_path 和 proxy_cache 名称是否严格一致:比如 keys_zone 定义为
mycache:10m,location 中必须写proxy_cache mycache,拼错或漏配都会让整个缓存模块静默失效
检查缓存键是否受客户端干扰
同一个资源被不同浏览器当成“不同资源”缓存,往往是因为缓存键混入了易变字段:
- 默认情况下,Nginx 的
proxy_cache_key可能包含$http_accept_encoding,导致 gzip 和 identity 版本被分别缓存——即使文件内容完全一样,也变成两个缓存项 - 应在 location 中显式固化 key:
proxy_cache_key "$host$request_uri";
确保所有请求对同一 URI 都走同一个缓存槽位 - CDN 或上游网关若自动添加或改写
Accept-Encoding,需在回源前统一抹除该头,避免污染缓存决策
验证响应头是否绕过或抑制了缓存校验
即使缓存存在,浏览器或中间层也可能因响应头跳过更新逻辑:
- 检查 Nginx 返回的 Cache-Control 是否含
no-cache或must-revalidate:这类指令会让浏览器强制发起条件请求,但若后端未正确返回304 Not Modified,就会误拉旧内容 - 确认 ETag 是否由 Nginx 生成而非透传后端:若后端返回固定 ETag(如硬编码值),即使文件更新,ETag 不变,协商缓存就永远不触发更新
- 前端请求若带
Cache-Control: no-cache或Pragma: no-cache,Nginx 默认跳过缓存逻辑,stale 或验证机制均不生效——需用proxy_ignore_headers Cache-Control;主动忽略干扰头
快速验证是否来自缓存及版本一致性
加一条响应头,直观看清来源:
- 在 location 中添加:
add_header X-Cache-Status $upstream_cache_status; - 访问资源时观察响应头:
→X-Cache-Status: HIT表示用了缓存
→X-Cache-Status: MISS表示未命中,可能 key 不对或缓存未启用
→X-Cache-Status: EXPIRED或STALE表示缓存存在但已过期或降级使用 - 配合
stat /path/to/static/app.js查看后端文件 mtime,对比响应头中Last-Modified是否一致,可快速定位是挂载延迟还是缓存策略问题











