怎么通过 `proxy_cache_bypass` 实现缓存穿透与绕过

发布于 2026-06-11 10:12:37 / 33人查看

proxy_cache_bypass 不是“穿透”缓存的技术,而是让 Nginx 主动跳过缓存查找、直接回源的控制指令。所谓“缓存穿透”在这里是误用术语;真正目标是:对特定请求,不读缓存、不依赖旧内容、直连后端——这叫“缓存绕过”。

要让它真正起作用,得满足三个硬条件:缓存已启用、变量写对、值判准。缺一不可。

必须在同一 location 中启用 proxy_cache

只写 proxy_cache_bypass 没用。Nginx 会直接忽略它,除非对应 location 明确配置了:

  • proxy_cache mycache;(名称需与 proxy_cache_pathkeys_zone 一致)
  • proxy_cache_valid 200 5m;(定义哪些响应可缓存)

例如:

location /api/ {
    proxy_pass http://backend;
    proxy_cache mycache;
    proxy_cache_valid 200 302 5m;
    proxy_cache_bypass $http_x_refresh;
}

变量名必须严格匹配 Header 规则

客户端发 X-Refresh: 1,Nginx 内部变量是 $http_x_refresh,不是 $http_X_Refresh$http_xrefresh。规则是:

  • 全小写
  • 短横 - 替换为下划线 _
  • 前缀固定为 $http_

常见对照:

  • X-Bypass-Cache$http_x_bypass_cache
  • X-Gray-Preview$http_x_gray_preview
  • X-Refresh-Cache$http_x_refresh_cache

值判断逻辑:非空且不等于 "0""off"

只要变量展开后满足以下任一,就触发绕过:

  • 是非空字符串(如 "true""1""force""debug"
  • 不是 "0"(不区分大小写)
  • 不是 "off"(不区分大小写)

比如这些都有效:

  • curl -H "X-Refresh: 1" /api/data
  • curl -H "X-Refresh: true" /api/data
  • curl -H "X-Refresh: T" /api/data

而这些无效:

  • curl /api/data(Header 缺失 → 变量为空)
  • curl -H "X-Refresh: 0" /api/data
  • curl -H "X-Refresh: off" /api/data

多条件组合与精细控制

支持空格分隔多个变量,任意一个为真即绕过:

  • 按 Header 或参数触发:
    proxy_cache_bypass $http_x_preview $arg_debug;
    (带 X-Preview 头,或 URL 含 ?debug=1

  • 按 IP 白名单限制:
    proxy_cache_bypass $http_x_refresh $remote_addr ~^(192\.168\.10\.);

  • map 做语义化判断(推荐用于灰度/角色):

    map $cookie_role $skip_cache {
        "admin" 1;
        default 0;
    }
    proxy_cache_bypass $skip_cache;

生产中务必搭配 proxy_no_cache

proxy_cache_bypass 只控制“不读缓存”,默认仍会把回源响应写入缓存(只要状态码和头符合缓存规则)。这可能导致敏感数据污染缓存。

所以关键补充是:

proxy_no_cache $http_x_refresh;

两个指令变量名必须完全一致,才能确保:
✅ 不查旧缓存
✅ 不存新响应

否则一次管理员调试请求,可能把带 Token 的用户数据缓存下来,被后续无权限用户命中。

验证是否生效:看响应头和日志

别只信配置,实测才可靠:

  • 发起带头请求:
    curl -I -H "X-Refresh: 1" https://yoursite.com/api/data

  • 检查响应头:
    应不含 X-Cache: HIT;理想是看到 X-Cache-Status: BYP(需提前加 add_header X-Cache-Status $upstream_cache_status;

  • 查 access log:
    $upstream_cache_status 字段应显示 BYP,不是 HITMISS

不复杂但容易忽略