缓存绕过导致源站压力过大,本质是请求未被边缘或代理层拦截而全部穿透至后端;需先验证X-Cache-Status是否MISS/BYPASS,再排查Cookie、响应头、query参数、cache_bypass配置及爬虫UA等五类绕过原因,并在Nginx、缓存层、应用层分层限流。

缓存绕过导致源站压力过大,本质是本该被边缘或代理层拦截的请求,全部穿透到了后端服务。排查要从“有没有缓存”和“为什么没缓存”两步入手;限流则需分层设防——在缓存层、Nginx 代理层、应用入口层各守一关。
一、快速确认是否真发生了缓存绕过
别只看配置,要看运行时行为:
- 用 curl -I 请求目标 URL,检查响应头中 X-Cache-Status(或 X-Cache)字段:出现 MISS 或 BYPASS 就说明没走缓存
- 查 Nginx access 日志,确认 $upstream_cache_status 字段:持续出现 MISS/EXPIRED/BYPASS 且占比高,就是缓存未生效
- 进缓存目录(如 /var/cache/nginx),用 find . -type f | wc -l 看缓存文件数量是否长期为 0 或极少
二、定位绕过原因:常见五类典型场景
多数绕过不是配置漏了,而是被隐式触发:
- 带 Cookie 或鉴权头:Nginx 默认对含 Cookie、Authorization、Cache-Control: no-cache 的请求不缓存。前端调试留下的 cookie、SDK 自动加的 token 都会触发 bypass
- 上游响应头禁止缓存:后端返回了 Cache-Control: private、no-store 或带 Set-Cookie,Nginx 就不会缓存。可用 proxy_hide_header Set-Cookie + proxy_cache_valid 强制覆盖
-
query string 导致 cache_key 不一致:比如 ?t=123456、?_=
这类动态参数,让每次请求 URI 都不同。应规范化 URI,用 set $key_uri $uri 剔除无关参数再构造 key - 缓存规则被显式跳过:检查 proxy_cache_bypass 和 proxy_no_cache 指令,确认是否误配了 $arg_nocache 或 $cookie_debug 等变量
- 爬虫 UA 被特殊放行或忽略:日志里发现大量 DataCrawler/1.0、Bytespider 等请求,且都回源,大概率是缓存策略默认不缓存带 query 的爬虫请求
三、源站压力大时的分层限流策略
缓存失效后,限流不是补救,而是兜底防线:
-
Nginx 层限流(最快生效):用 limit_req_zone 对 IP 或请求路径限速。例如按 $binary_remote_addr 限 10r/s,突发允许 20 个请求:
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_req zone=perip burst=20 nodelay; - 缓存层兜底限流(推荐):CDN 或 Nginx proxy_cache 配合 proxy_cache_lock on,避免缓存未命中时多个相同请求同时打到后端;再加 proxy_cache_lock_timeout 5s 防止锁太久
- 应用层接口级限流:对高频 API(如商品列表 /api/v2/products)做 QPS 限制,Spring Boot 可用 Resilience4j 或 Sentinel;Go 可用 golang.org/x/time/rate
- 恶意流量定向拦截:识别出异常 UA 或固定路径遍历行为(如 page=1~5000),在 Nginx 中用 if ($args ~ "page=[0-9]+") { return 429; } 快速拦截
四、验证与长期防护建议
改完配置后必须验证效果:
- 用 ab 或 wrk 模拟并发请求,观察源站连接数、Nginx 缓存命中率、后端 QPS 是否回落
- 开启 error_log /var/log/nginx/error.log debug(临时),查看是否有 "cache lock failed" 或 "invalid cache control" 类报错
- 把爬虫、调试流量、健康检查等非用户请求,统一打上标记(如 X-Source: crawler),在缓存规则中明确排除或降级处理











