log_subrequest指令需显式开启才能记录子请求日志,其默认关闭、不继承、仅作用于本作用域内主动发起的子请求(如ssi、auth_request),不适用于try_files等内部重定向。

log_subrequest 指令必须显式开启才能记录子请求
默认情况下 Nginx 完全不记录 subrequest(包括 SSI include、auth_request、mirror、内部重定向如 try_files 后的 fallback)的日志。即使你配置了 access_log,这些子请求也不会出现在日志里——这不是漏配路径或格式问题,而是设计如此。
必须在 http、server 或 location 块中显式启用:
log_subrequest on;
注意:log_subrequest off 是默认值;该指令不继承,即父块开了,子 location 仍需单独声明(除非用 http 级统一开)。
- 只对本作用域内发起的子请求生效(例如在
location /api/下开,不会记录/static/里的 SSI 子请求) - 开启后,子请求会复用主请求的
access_log配置,但会在$request和$status等变量中体现其真实路径与状态 - 不增加额外日志文件,也不改变日志格式语法——你只需确保日志格式里包含能区分主/子请求的字段(如
$request_id、$pid、$connection)
用 $request_id 关联主请求与所有子请求链
单靠时间戳或连接 ID 很难准确串联一次用户请求触发的完整子请求树(尤其高并发下),$request_id 是最可靠锚点。它由 Nginx 自动生成(需 1.11.0+),同一主请求及其所有子请求共享同一个 ID。
启用方式很简单:
log_format main '$request_id - $remote_addr - [$time_local] "$request" $status $body_bytes_sent'; access_log /var/log/nginx/access.log main;
然后在任意子请求中(比如 SSI include 的 /_fragment/header.html),$request_id 值和父请求完全一致。你可以用如下命令快速验证:
tail -f /var/log/nginx/access.log | grep "your-test-request-id"
- 如果没看到子请求行,先确认
log_subrequest on是否在对应 location 生效 -
$request_id在子请求中不可被覆盖或重设,安全可靠 - 若用 OpenResty,也可用
ngx.var.request_id在 Lua 中读取,用于打点或透传到后端
SSI 子请求的 status 和 request 变量行为容易误判
当使用 ssi on 并执行 <!--# include virtual="/_inc/footer.html" --> 时,该子请求会走完整 Nginx 请求处理流程,但它返回的状态码、响应体长度、甚至是否命中缓存,都可能和直连该路径的结果不同——因为子请求复用主请求的上下文(如 headers、variables、auth state)。
典型表现:
- 子请求返回
404,但直接访问/_inc/footer.html是200→ 很可能是子请求未继承root或alias,需在子请求 location 显式定义 -
$request在子请求日志中显示为GET /_inc/footer.html HTTP/1.0(注意是 HTTP/1.0,且无原始 query string)→ 子请求不自动携带主请求的$args,需手动拼接 -
$upstream_status对 SSI 子请求始终为空,因为它不经过 upstream 模块;要用$status看最终响应状态
调试建议:在子请求 location 中临时加 return 200 "uri=$uri args=$args\n";,对比主/子请求变量差异。
内部重定向(如 try_files)产生的子请求不触发 log_subrequest
这是最容易踩坑的一点:try_files、error_page 404 = @fallback、rewrite ... last 这类内部重定向(internal redirect)**不属于 subrequest**,它们只是请求 URI 和处理阶段的跳转,不创建新请求上下文,因此 log_subrequest on 对它们完全无效。
想追踪这类流程,只能靠:
- 开启
error_log /path/debug;并设级别为debug(注意性能损耗极大,仅临时用) - 在关键 location 中用
add_header X-Flow-Step "stage-2";逐层标记,再从 access log 的$sent_http_x_flow_step提取 - 用 OpenResty 的
ngx.ctx在 rewrite 阶段写标记,最后在 log_by_lua 中输出完整路径链
简单说:log_subrequest 只管「主动发起」的子请求(SSI、auth_request、mirror、lua-resty-http 调用等),不管「被动跳转」的内部重定向。混淆这两者会导致日志分析完全跑偏。
复杂点在于,一个真实页面可能同时含 SSI 子请求 + try_files 内部跳转 + auth_request 子请求——三者日志行为完全不同,必须分开建模分析。别指望一条 log_subrequest on 解决所有“请求链路”问题。











