要准确分析nginx缓存状态,需在log_format中添加$upstream_cache_status并启用对应access_log,配合add_header便于调试;其状态如hit、miss、expired等含义各异,须结合上下文区分;用awk可统计命中率及带宽节省率,但需过滤head/304、gzip压缩等因素干扰。

要准确分析 Nginx 日志里的缓存状态(如 HIT、MISS),核心是让日志记录 $upstream_cache_status 变量,并按其值做结构化统计。光看状态字符串本身没用,必须结合请求上下文和数据聚合才有实际意义。
确保日志中包含缓存状态字段
默认 access_log 不输出缓存状态,需手动在 log_format 中加入:
- 在
http或server块中定义日志格式,例如:log_format cache_log '$remote_addr [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $upstream_cache_status $upstream_response_time'; - 在对应 location(尤其是代理静态资源或后端服务的块)中启用该格式:
access_log /var/log/nginx/cache.log cache_log; - 建议同时加响应头便于调试:
add_header X-Cache-Status $upstream_cache_status;,这样用curl -I就能快速验证单次请求行为
区分有效命中与干扰状态
$upstream_cache_status 的取值不止 HIT 和 MISS,不同状态代表不同缓存行为,不能简单归为“成功/失败”:
- HIT:缓存新鲜且直接返回,是真正节省带宽和后端压力的有效命中
- EXPIRED:缓存存在但已过期,仍会回源验证或更新——属于“伪命中”,实际产生回源成本
- STALE:后端不可用时返回过期内容,反映的是上游稳定性问题,不是缓存配置问题
-
BYPASS:被
proxy_cache_bypass规则跳过,常见于带no-cache头、特定 Cookie 或参数的请求 -
MISS:首次访问或 key 不匹配(比如 URL 含 utm 参数但未从 key 中排除),需检查
proxy_cache_key是否合理
用命令行快速统计命中率
日志写入后,可用 awk 等工具按状态计数:
- 统计各状态出现次数:
awk '{print $10}' /var/log/nginx/cache.log | sort | uniq -c | sort -nr
(假设$upstream_cache_status是第 10 字段) - 计算基础命中率(仅 HIT / 总请求数):
awk '$10=="HIT"{hit++} END{print "HIT rate:", hit/NR*100 "%"}' /var/log/nginx/cache.log - 若关注带宽节省效果,应聚合
$body_bytes_sent:
分别对 HIT 和 MISS 行求和,再算 MISS 流量占总响应流量的比例(即「下行带宽节省率」)
排除影响统计准确性的因素
以下情况会让日志中的状态或字节数失真,分析前建议过滤:
- HEAD 请求或 304 响应的
$body_bytes_sent为 0,但它们也可能是 HIT;如评估“完整响应体节省”,应排除status == 304或request_method == "HEAD"的行 - 开启 gzip 时,
$body_bytes_sent是压缩后体积;若 MISS 请求由后端返回未压缩内容、Nginx 缓存前压缩,则 HIT 的字节数会显著偏小——这部分属于额外压缩收益,可结合$sent_http_content_encoding单独标记 - 确保后端响应含有效的
Cache-Control或Expires,且 Nginx 的proxy_cache_valid配置未被覆盖,否则 HIT 可能不稳定、不可信











