必须用可观测手段验证缓存键隔离效果:通过x-cache-status响应头、带业务维度的定制日志、缓存文件md5路径比对及交叉请求压力测试,确认不同租户/设备请求命中各自独立缓存槽且内容不串扰。

验证 Nginx 缓存键在复杂业务场景下的隔离效果,不能只看配置是否写对,关键得用可观测手段确认“不同请求确实进了不同缓存槽、互不干扰”。重点不是“理论上该隔离”,而是“实际运行中真隔离”。
看响应头:X-Cache 和 X-Cache-Status 是第一眼证据
Nginx 会通过 $upstream_cache_status 变量暴露缓存行为,必须显式透出:
add_header X-Cache "$upstream_cache_status"; add_header X-Cache-Key "$scheme$request_method$host$uri$is_args$args$cache_uid_$cache_device"; // 可选:把实际生成的 key 也打出来(调试期用,上线建议关)
-
HIT表示命中——但要确认是“本租户/本设备的 HIT”,不是误命了别人的内容; -
MISS后紧接HIT,说明同一用户连续请求已成功复用; - 若 A 用户请求返回
HIT,B 用户相同 URI 却返回MISS→ 初步说明隔离有效; - 若两者都
HIT且内容一致 → 很可能 key 没带区分维度,存在污染风险。
查日志:用定制日志格式抓取真实维度与状态
光看响应头不够,需关联请求上下文。在 http 块中定义带业务字段的日志格式:
log_format cache_debug '$remote_addr - $tenant_id [$time_local] '
'"$request" $status $body_bytes_sent '
'$upstream_cache_status "$http_x_tenant_id" '
'"$cache_uid" "$cache_device" "$cache_key_suffix"';
然后在 location 中启用:
access_log /var/log/nginx/cache_debug.log cache_debug;
- 日志里出现
t-101和t-202的请求,各自upstream_cache_status都是HIT,且cache_uid值不同 → 验证了租户级隔离; - 同一
tenant_id下,mobile设备返回HIT,desktop返回MISS→ 说明cache_device参与了 key 构建并生效; - 若某类请求始终
MISS,检查日志中$cache_uid或$cache_device是否为空(比如正则没匹配上),这是常见失效点。
对比缓存文件:直接看磁盘是否物理隔离
Nginx 缓存最终落地为文件,路径由 proxy_cache_path 的 levels 和 keys_zone 决定。可手动抽样验证:
- 找两个明显应隔离的请求(如
uid=1001&device=mobile和uid=1002&device=desktop); - 在日志中查出它们对应的
$request_uri和生成的$cache_key_suffix; - 进入缓存目录(如
/var/cache/nginx/prod/),用find . -name "*$(echo -n "GET http://a.com/api/user $cache_key_suffix" | md5sum | cut -d' ' -f1)*"定位缓存文件; - 若两个请求生成的 MD5 前缀落在完全不同子目录(如
c/2a/xxxvsf/8e/xxx),且文件内容curl -s对比确实不同 → 物理层已隔离。
模拟交叉请求:主动制造冲突场景做压力验证
配置写完不等于可靠,要刻意“破坏”一次:
- 用工具(如
ab或hey)并发发送两组请求:- 组 A:
Cookie: uid="1001"; device="mobile" - 组 B:
Cookie: uid="1001"; device="desktop"
- 组 A:
- 观察:
- A 组首次全
MISS,第二次全HIT; - B 组首次也全
MISS(说明没蹭 A 的缓存); - A 组修改响应体(如后端返回
"device":"mobile"),B 组仍返回"device":"desktop"→ 内容未串扰。
- A 组首次全
注意几个典型失效信号
- 所有请求
upstream_cache_status都是BYPASS:检查proxy_cache_bypass条件是否误配(比如$cookie_nocache总是非空); - 日志中
$cache_uid字段大量为guest或空:map正则未覆盖实际 Cookie 格式(如后端写的是UID=1001而非uid=1001); - 同一
X-Tenant-ID下,不同 IP 请求交替出现HIT/MISS:可能proxy_cache_key漏掉了$remote_addr(仅当需 IP 级隔离时才加,一般不推荐); - 缓存文件存在但响应体始终是旧的:
proxy_cache_valid时间太长,或后端返回了Cache-Control: max-age=3600被 Nginx 尊重,需检查proxy_ignore_headers设置。
不复杂但容易忽略











