最稳妥的一键绕过方案是用 X-Refresh 请求头配合 proxy_cache_bypass $http_x_refresh,但必须同时启用 proxy_cache、合理设计 proxy_cache_key,且需严格控制触发权限;仅写 proxy_cache_bypass 无效,因其生效前提是对应 location 已启用缓存,且变量名必须为 $http_x_refresh(大小写敏感、短横变下划线),值非空且不等于 "0" 或 "off" 才触发。

直接给结论:用 X-Refresh 请求头配合 proxy_cache_bypass $http_x_refresh 是最稳妥的一键绕过方案,但必须同时启用 proxy_cache、合理设计 proxy_cache_key,且不能忽略触发来源的权限控制。
为什么只写 proxy_cache_bypass 没用
这个指令本身不生效的前提是:对应 location 必须已启用缓存。常见错误是只在 http 块写了 proxy_cache_bypass $http_x_refresh,但没在实际转发的 location 里加 proxy_cache mycache——此时 bypass 完全被忽略。
还要注意变量命名规则:X-Refresh 头在 Nginx 内部对应变量名是 $http_x_refresh(短横变下划线、全小写),写成 $http_x_refresh 或 $http_X_Refresh 都会失效。
- 值判断逻辑:只要
$http_x_refresh非空且不等于"0"、"off",就触发 bypass - 不区分大小写值:比如
X-Refresh: TRUE、X-Refresh: 1、X-Refresh: t都有效 - 空字符串或缺失该头,则走正常缓存流程
proxy_cache_bypass 和 proxy_no_cache 的区别必须分清
前者只跳过缓存读取,响应仍可能被写入缓存;后者才真正阻止响应进缓存。生产中常需两者共用:
proxy_cache_bypass $http_x_refresh;proxy_no_cache $http_x_refresh;
这样带 X-Refresh 的请求既不读旧缓存,也不把新响应存进去——适合调试、灰度、敏感接口校验等场景。
- 如果只配 bypass 不配 no_cache,后端返回了
Cache-Control: public, max-age=3600,Nginx 仍会把它缓存下来 - 如果只配 no_cache 不配 bypass,请求还是会先查缓存(命中就直接返回),达不到“实时回源”效果
- 两者变量要一致,否则逻辑断裂
绕过缓存后怎么确保新内容能进缓存(可选但关键)
很多团队以为 bypass 就是“刷新”,其实它只是让这一次请求绕开缓存;是否把这次响应存回去,取决于 proxy_cache_valid 和响应状态码是否匹配。
例如你配置了 proxy_cache_valid 200 5m,但后端返回的是 201 Created,那这次响应根本不会进缓存——即使你刚 bypass 回源拿到最新数据。
- 务必检查
proxy_cache_valid是否覆盖了你的业务响应码(如 200、201、302、404) - 若后端返回
Cache-Control: no-cache,默认会阻止缓存;此时需加proxy_ignore_headers Cache-Control强制由 Nginx 控制 - 验证方式:curl -H "X-Refresh: 1" URL 后,看响应头是否有
X-Cache-Status: MISS或BYP,再发一次无头请求确认是否变成HIT
安全限制:别让任意 IP 都能触发 bypass
线上环境绝不能开放 X-Refresh 给所有客户端。容易被滥用,甚至成为 DoS 攻击入口。
推荐组合判断方式:
proxy_cache_bypass $http_x_refresh$remote_addr ~^(192\.168\.10\.|10\.0\.0\.);
这样只有内网 IP + X-Refresh 头才触发。更严格的做法是结合 map 指令做白名单:
map $http_x_refresh $bypass_flag { default 0; "~^[tT1]" $remote_addr ~^(192\.168\.10\.|10\.0\.0\.);}proxy_cache_bypass $bypass_flag;
复杂点在于:绕过逻辑一旦上线,就很难靠日志快速定位是谁触发的——建议在 access log 中显式记录 $upstream_cache_status 和 $http_x_refresh。











