nginx proxy_cache 与 lua 脚本冲突源于阶段介入时机错位、缓存 key 变量污染及 bypass 逻辑未对齐;access_by_lua 适合控制缓存开关但不可修改 key 变量,content_by_lua 默认绕过缓存,header_filter_by_lua* 不影响本次缓存策略。

排查 Nginx proxy_cache 与 Lua 脚本(如通过 ngx_lua 模块)的执行冲突,核心在于理清二者在请求生命周期中的介入时机、共享状态限制及常见干扰点。缓存决策(是否命中/跳过/更新)和 Lua 执行(如 access_by_lua*、content_by_lua*、header_filter_by_lua*)若配置不当,容易导致缓存行为异常(如缓存了不该缓存的响应、未执行 Lua 逻辑、或 Lua 读取到错误的缓存头)。
确认 Lua 阶段与缓存流程是否兼容
Nginx 缓存主要在 postread → server_rewrite → find_config → rewrite → post_rewrite → preaccess → access → post_access → try_files → content → log 这一链条中由 proxy_cache 相关指令驱动。关键冲突点如下:
-
access_by_lua*在缓存决策前执行:它运行在access阶段,早于proxy_pass和缓存判断(由proxy_cache_valid、proxy_cache_bypass等控制),适合做缓存开关控制(如根据请求头/参数决定是否绕过缓存);但若在此阶段修改了$scheme、$host、$request_uri等影响缓存 key 的变量,可能导致缓存 key 计算错位; -
content_by_lua*通常绕过缓存:一旦使用该指令替代proxy_pass,Nginx 就不再走 proxy 缓存流程(除非你手动调用ngx.location.capture并自行处理响应缓存); -
header_filter_by_lua*在缓存写入后才运行:它作用于 upstream 响应头已生成、且缓存文件已落盘(或即将落盘)之后,此时修改响应头(如Cache-Control)不会影响本次缓存策略,但会影响客户端看到的头 —— 若需控制缓存策略,必须在proxy_cache_valid或proxy_ignore_headers中配置,而非靠 header_filter 修改;
检查缓存 key 是否被 Lua 不知不觉污染
默认 proxy_cache_key 包含 $scheme$proxy_host$request_uri,任何 Lua 脚本中对这些变量的覆盖(例如 ngx.var.request_uri = "/api/v2/")都会改变实际参与缓存 key 计算的值,造成缓存碎片或击穿。
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
- 用
log_format记录真实 key:添加log_format cache_log '$remote_addr - $request_uri ⬇ $upstream_cache_status "$scheme://$host$request_uri" [$upstream_http_x_cache_key]';,并在 Lua 中用ngx.var.upstream_http_x_cache_key(需 upstream 返回该头)或自定义变量打日志比对; - 避免在
set_by_lua*或access_by_lua*中直接赋值给ngx.var.*影响缓存 key 的变量;如需重写 URI,优先用rewrite ... break;,并确保proxy_cache_key显式包含稳定字段(如加$arg_v版本号);
验证 Lua 是否意外禁用了缓存
以下情况会导致请求完全不进入缓存流程:
- 在
access_by_lua*中调用了ngx.exit(ngx.HTTP_OK)或类似终止流程的操作,使请求未走到proxy_pass; - 设置了
proxy_cache_bypass $lua_var,而 Lua 中将$lua_var设为非空(如set_by_lua $skip_cache 'return ngx.var.cookie_nocache and "1" or ""';),但漏写了proxy_cache_bypass对应逻辑,导致全部 bypass; - 响应中包含 Nginx 默认不缓存的头,如
Set-Cookie、Expires为过去时间、Cache-Control含no-store/private—— 即使 Lua 没动这些头,也要检查 upstream 是否返回了它们;可用proxy_ignore_headers Cache-Control Expires Set-Cookie;强制忽略(慎用);
启用详细日志定位执行顺序与状态
在 nginx.conf 的 http 块中开启调试级日志(仅测试环境):
- 添加
error_log /path/to/debug.log debug;; - 确保编译时启用了
--with-debug; - 关注日志中类似
cache file: "/var/cache/nginx/xxx", valid: 1(缓存命中)、cache miss、cacheable response、do not cache response及lua-access-phase、lua-content-phase等关键词; - 配合
add_header X-Cache-Status $upstream_cache_status;返回响应头,直观判断缓存状态(HIT/MISS/BYPASS);
不复杂但容易忽略:Lua 和 proxy_cache 本身不互斥,冲突几乎都源于阶段误用、变量污染或缓存策略配置与 Lua 行为未对齐。先明确“谁该决定缓存”(Nginx 原生指令 or Lua 逻辑),再统一 key 构造和 bypass 条件,问题就清晰了。










