Nginx proxy_cache 连续 MISS:响应和配置都没有缓存有效期
核对上游响应的有效期与缓存限制,为确认公开的 200 响应设置有限 TTL,并验证命中、身份隔离和内容更新行为。
Nginx 已配置 proxy_cache,相同 GET 请求却连续显示 MISS,缓存目录也没有预期对象。若上游响应没有提供可用的缓存有效期,而对应配置也没有 proxy_cache_valid,Nginx 就缺少这份响应可保持新鲜多久的依据。
本文限定已经确认可向所有访问者公开、内容不随用户身份变化的 HTTP 200 响应。登录页面、购物车、授权接口和含私人数据的响应不能为了得到 HIT 而套用此方案。缓存目录权限、缓存键不断变化、主动 bypass 等原因也会造成不命中,需要先区分。
先取得真正的上游响应头
对同一个公开资源,分别检查 Nginx 对外响应和上游实际返回内容。使用已有授权的诊断入口,保持相同的 Host、路径和请求参数。只看对外响应可能漏掉被 proxy_hide_header 隐藏的字段。
重点查看状态码,以及 X-Accel-Expires、Cache-Control、Expires、Set-Cookie、Vary。还需对照已经加载的配置,确认选中同一个 location、缓存 zone 和缓存键。
X-Accel-Expires 可以直接给出有效期,值为 0 会禁止缓存;没有它时,Expires 或 Cache-Control 可以提供相应缓存参数。响应头的缓存参数优先于 proxy_cache_valid。因此,已有明确禁止缓存的响应不能仅靠加一个有效期指令强行变成可缓存内容。
默认处理下,Set-Cookie 或 Vary: * 也会阻止缓存。不要仅因为客户端没有看到 Cookie 就排除这一项:隐藏响应头只控制向客户端传递,不等于禁止 Nginx 内部处理该头。已有 proxy_ignore_headers 时,更要明确哪些保护规则已经被关闭。
本问题的处理前提是:上游确实没有可用有效期,响应适合公共缓存,并且不存在上述拒绝缓存条件。若应用本应返回 private 或 no-store,先保留它的保护语义。
给批准缓存的 200 响应设置有限有效期
通常可以由应用在公共资源响应中提供准确的缓存策略。如果目前需要由 Nginx 管理有效期,在原有、已工作的公共资源 location 中增加:
proxy_cache_valid 200 60s;
这里的 60 秒是示例业务选择,不是通用最佳值。它表示在没有更高优先级响应头控制时,允许 200 响应按这个有效期缓存;不表示所有状态码都该缓存,也不需要改成 any。
下面展示相应 location 的结构,假设 public_cache 已由现有 proxy_cache_path 定义,且 /public/catalog.json 被确认是完全公开的资源:
location = /public/catalog.json {
proxy_pass http://127.0.0.1:8080;
proxy_cache public_cache;
proxy_cache_valid 200 60s;
proxy_cache_methods GET HEAD;
proxy_cache_bypass $http_authorization $http_cookie;
proxy_no_cache $http_authorization $http_cookie;
}
这是一段需合入现有 server 的示例,不是独立 Nginx 配置。保留原来的认证、限流、请求头和其他必要设置,不要为加有效期重建一个遗漏保护的代理块。示例的上游地址要替换为实际可达服务。
这两个指令在任一变量非空且不等于字符串 0 时,分别阻止读取缓存和写入缓存,适用于正常的非空凭据头。若业务把字面值 0 也当作身份标记,需要先规范化判断条件,不能直接依赖这段示例。它们不能证明剩余请求一定公开:应用还可能根据其他请求头、参数或来源返回不同内容,是否可共享必须由业务确认。这里显式限制为 GET/HEAD 缓存方法,不继承上层可能增加的 POST 缓存,也不删除请求体或用户差异来凑出相同缓存键。
用相同请求观察首次填充与后续命中
修改前保留原配置到不会被 include 加载的位置,先在对应实例的测试配置中验证,再按原部署流程加载:
nginx -t -c /etc/nginx/nginx.conf
检查成功后才安排 reload,配置路径和运行前缀应与实例一致。重载完成后,对相同的公共 URL 连续发送 GET,使用现有的 $upstream_cache_status 诊断记录观察结果。
如果临时通过响应头显示这个变量,要保留原有 add_header 安全头及继承行为,验收后移除诊断项;不要公开记录 Cookie、Authorization 或响应中的私人数据。
在 proxy_cache_min_uses 使用默认值、没有其他变化的简单场景中,首次请求可能为 MISS,完成缓存后同键请求才可能为 HIT。不能只看到一次 MISS 就宣布失败;同样,单次 HIT 也不证明用户隔离和内容更新正确。仍不命中时继续查错误日志、目录权限、缓存键和 bypass 条件,不盲目增加更多 proxy_ignore_headers。
更新策略必须接受这段新鲜期
加入 60 秒有效期后,上游内容更新并不意味着缓存对象马上失效。在这段新鲜期内,访问者可能仍读到旧内容。需要立即生效的数据应采用业务认可的版本化 URL、失效机制或不缓存策略,而不是单纯把所有响应的 TTL 加长。
验收除了检查 HIT,还要在测试环境核对内容更新后的行为,以及带身份信息的请求不会误读公共缓存。保留已有 ETag、Last-Modified、Vary 等协商语义;本次只修缺少有效期,不顺带关闭其他缓存控制。
回退时恢复原配置并按同样流程验证、加载。已经写入的缓存对象不会仅因为删掉这条指令就立即全部消失;若要求立刻停止服务该缓存,应按原部署方案对该 location 禁用缓存,或采用已有的精确失效流程。不要直接清空其他应用共用的缓存目录。


