问题根源在于浏览器是否收到并执行缓存头,需分三层排查:响应头是否发出、浏览器是否接收理解、是否被中间层覆盖;验证需用curl看实际响应头、检查304条件、确认expires适用范围、排除开发者工具禁用缓存等干扰,并通过x-cache-status和chrome network面板实测命中情况。

客户端不遵守缓存策略、反复发起本该命中的请求,问题往往不在 Nginx 配置本身,而在于浏览器是否真正收到了你设定的缓存头,以及它是否按预期执行了缓存逻辑。排查要从“响应头是否发出”“浏览器是否接收并理解”“是否被中间层覆盖或绕过”三层切入。
确认 Nginx 是否真返回了预期缓存头
不能只看配置文件写了 add_header,得验证实际响应中有没有:
- 用 curl -I http://your-domain/path 查看原始响应头,重点找 Cache-Control、Expires、ETag、Last-Modified
- 若返回的是 304(Not Modified),说明浏览器发了条件请求,此时响应体为空,但必须有 ETag 或 Last-Modified 才能触发协商缓存;若连这两个头都没有,浏览器只能重新请求
- 检查是否误用了 expires -1:它对 200 响应有效,但对 302/301/5xx 等状态码无效,这些响应不会应用 expires 指令
检查浏览器是否忽略或未识别缓存指令
浏览器行为受多种因素干扰,尤其在开发调试阶段:
- 开发者工具 Network 面板中勾选 Disable cache 后再刷新——如果此时请求变少、出现 304,说明之前是被禁用缓存导致重复请求
- 用户手动按 Ctrl+F5(强制刷新) 会跳过所有缓存,直接发新请求;普通 F5 只跳过强缓存但可能走协商缓存
- Chrome 对带 Cache-Control: max-age 的资源,在用户点击地址栏回车或跳转时会强缓存,但在主动刷新时仍可能发 If-None-Match 请求——若后端没返回 ETag,就无法 304,只能重下
排查中间层或上游干扰缓存行为
浏览器看到的响应,未必是你 Nginx 直接生成的:
- CDN(如 Cloudflare)可能覆盖了你的 Cache-Control,需登录后台检查该路径的缓存等级和是否启用“尊重源站头”
- 若 Nginx 启用了 proxy_cache,且上游服务返回了 Cache-Control: no-cache 或 private,Nginx 默认会跳过缓存(除非显式配置 proxy_ignore_headers)
- 后端应用(如 PHP、Node.js)若在响应中写入了冲突头(例如同时设了 Cache-Control: public 和 Expires),以 Cache-Control 为准,但某些老旧客户端可能只认 Expires
验证缓存是否真正生效的实操方式
光看配置和头不够,要观察真实命中行为:
- 在对应 location 中添加:add_header X-Cache-Status $upstream_cache_status;,然后 curl 测试,看返回是 HIT / MISS / BYPASS
- 打开 Chrome Network 面板,刷新页面,找目标资源:若 Size 列显示 (from memory cache) 或 (from disk cache),说明强缓存生效;若显示具体字节数且 Time 很小(
- 对静态资源(JS/CSS),确保构建时已加入内容哈希(如 app.a1b2c3.js),并在 Nginx 中配 expires max; add_header Cache-Control "public, immutable";,这样刷新也不会触发验证











