排查 nginx 缓存有效期失效不生效,核心在于确认请求是否命中了本地磁盘或内存中的缓存文件,以及检查响应头中 cache-control 和 last-modified 字段是否符合预期。通常做法是开启 proxy_cache 并设置合理的 proxy_cache_valid,同时通过浏览器开发者工具或 curl -i 查看返回的 x-cache 状态(如 hit/miss)及 http 状态码(200 ok 而非 304 not modified)。如果配置正确但依然频繁 miss,需检查上游服务器是否强制发送了 no-cache 或 private 指令,或者 nginx 的缓存路径权限不足导致写入失败。

如何验证 Nginx 是否真正使用了缓存
要验证 Nginx 是否真正使用了缓存,最直接的方法是检查响应头中的自定义字段和 HTTP 状态码。在 Nginx 配置中,可以通过 add_header X-Cache $upstream_cache_status; 添加一个名为 X-Cache 的头部,其值会显示为 HIT(命中)、MISS(未命中)或 BYPASS(绕过)。当用户首次访问时,Nginx 向后端请求资源并缓存,此时 X-Cache 为 MISS;再次访问相同资源时,若缓存未过期,Nginx 直接返回本地副本,X-Cache 变为 HIT,且 HTTP 状态码通常为 200。如果始终显示 MISS,则说明缓存机制未生效或配置有误。

server {
listen 80;
server_name example.com;
# 定义缓存区域:名称=one, 大小=10m, 过期时间=1天
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:10m max_size=1g inactive=60m use_temp_path=off;
location / {
proxy_pass http://backend_server;
# 启用缓存
proxy_cache one;
# 设置不同状态码的缓存时间
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# 添加自定义头部以查看缓存状态
add_header X-Cache $upstream_cache_status;
# 确保客户端也能缓存(可选,取决于需求)
add_header Cache-Control "public, max-age=600";
}
}
上述配置中,proxy_cache_path 定义了缓存存储的路径和基本参数。levels=1:2 表示目录层级结构,有助于提高文件系统性能。keys_zone=one:10m 在共享内存中保存键值对映射,用于快速查找缓存。max_size=1g 限制磁盘占用上限。inactive=60m 表示如果缓存文件在 60 分钟内未被访问,将被删除。在实际操作中,应确保 /var/cache/nginx 目录存在且 Nginx 工作进程有读写权限。通过 curl -I http://example.com/test.jpg 命令可以反复测试,观察 X-Cache 从 MISS 变为 HIT 的过程,从而确认缓存功能正常运作。
为什么上游服务器返回的指令会导致缓存失效
即使 Nginx 配置了缓存,如果上游应用服务器(如 Tomcat、Node.js 或 PHP-FPM)在响应头中明确指示不要缓存,Nginx 也会尊重这些指令而忽略自身的 proxy_cache_valid 设置。常见的导致缓存失效的上游指令包括 Cache-Control: no-cache、Cache-Control: no-store、Cache-Control: private 以及 Vary: Cookie 等。特别是 no-store 会禁止任何存储,而 private 通常意味着内容针对特定用户,不适合被公共代理缓存。此外,如果上游服务器根据请求中的 Cookie 动态生成内容,但未正确设置 Vary 头,Nginx 可能会错误地缓存个性化内容,导致其他用户看到错误数据,或者因为检测到 Cookie 变化而拒绝缓存。

# 在 Nginx 配置中强制覆盖上游的缓存控制指令
location / {
proxy_pass http://backend_server;
proxy_cache one;
# 关键步骤:清除上游可能设置的禁止缓存头
proxy_hide_header Cache-Control;
proxy_hide_header Expires;
# 重新设置 Nginx 自己的缓存控制头
add_header Cache-Control "public, max-age=3600";
# 处理 Vary 头,避免基于 Cookie 的缓存混乱
# 如果业务允许忽略 Cookie 进行缓存,可以移除 Vary 头
proxy_hide_header Vary;
}
为了克服上游服务器的干扰,可以在 Nginx 中使用 proxy_hide_header 指令来屏蔽上游传来的 Cache-Control、Expires 和 Vary 等头部信息。随后,使用 add_header 指令显式地为响应添加新的 Cache-Control 头,例如设置为 public, max-age=3600,强制客户端和中间代理(包括 Nginx 自身)缓存该资源一小时。这种方法适用于那些无法修改后端代码,但需要对其静态或半静态内容进行缓存的场景。需要注意的是,如果内容是高度个性化的(如用户仪表盘),强行覆盖缓存策略可能导致数据泄露或不一致,因此仅在对内容无敏感性的场景下使用此技巧。通过这种方式,可以确保 Nginx 的缓存策略拥有最高优先级,从而解决因上游指令冲突导致的缓存失效问题。
当请求包含特定 Cookie 时缓存为何不生效
当请求中包含 Cookie 时,Nginx 默认不会使用缓存,这是为了防止将针对特定用户的个性化内容缓存下来供其他用户使用。这种行为由 proxy_cache_bypass 和 proxy_no_cache 指令控制,默认情况下,如果请求头中包含 Cookie,Nginx 会认为该请求是“私有的”,从而跳过缓存直接转发给后端。对于希望缓存静态资源(如图片、CSS、JS)但又不想完全禁用 Cookie 检测的场景,可以通过调整 proxy_cache_key 来排除 Cookie 的影响,或者使用 map 指令根据 Cookie 的值决定是否绕过缓存。
http {
# 定义一个变量,判断是否应该绕过缓存
map $http_cookie $cache_bypass {
default 0; # 没有 Cookie 或 Cookie 不包含特定值,不绕过
~"session_id=" 0; # 如果 Cookie 中包含 session_id,也不绕过(视业务而定)
"" 0; # 空 Cookie 不绕过
}
server {
location / {
proxy_pass http://backend_server;
proxy_cache one;
# 使用自定义的缓存键,忽略 Cookie
proxy_cache_key "$scheme$request_method$host$request_uri";
# 根据变量决定是否绕过缓存
proxy_cache_bypass $cache_bypass;
proxy_no_cache $cache_bypass;
add_header X-Cache $upstream_cache_status;
}
}
}
在上述配置中,proxy_cache_key 被修改为仅包含协议、方法、主机名和 URI,去除了默认的 $cookie_jsessionid 或其他 Cookie 变量。这意味着无论请求携带何种 Cookie,只要 URL 相同,Nginx 就会尝试使用相同的缓存键。配合 proxy_cache_bypass 和 proxy_no_cache











