如何深度追踪并分析SSI、镜像流量及内部重定向的子请求执行细节?

2026-04-27 22:251阅读0评论SEO教程
  • 内容介绍
  • 相关推荐

本文共计923个文字,预计阅读时间需要4分钟。

如何深度追踪并分析SSI、镜像流量及内部重定向的子请求执行细节?

plaintextlog_subrequest 仅在定义了能识别子请求的日志变量(如 $subrequest)且日志格式正确引用时才生效。未配置 log_format 或 access_log 指令,log_subrequest on 等同于未设置。

为什么 $subrequest 总是空?

这是最常卡住人的点:$subrequest 变量本身不输出任何内容,它只在子请求上下文中为字符串 "1",主请求中为空字符串(不是 "0",也不是 NULL)。所以如果你在日志里看到 subreq="",那大概率是主请求;看到 subreq="1",才是子请求。

  • 必须启用 log_subrequest on,该指令需放在 httpserverlocation 块中,作用域决定是否生效
  • $subrequest 仅由 ngx_http_core_module 提供,无需额外编译,但 Nginx 版本需 ≥ 1.7.9(2014 年后版本基本都满足)
  • 日志格式中若用了 $subrequest 却没开启 log_subrequest,该字段会始终为空,且不会报错

SSI 包含和 mirror 指令产生的子请求怎么区分?

两者都会触发子请求,但协议头、请求方法和来源路径明显不同,靠 $request + $subrequest + $http_user_agent 组合就能分辨:

  • SSI 子请求:协议固定为 HTTP/1.0$request_methodGET$request_uri 是被 include 的路径(如 /ssi/header.html),$http_user_agent 为空
  • mirror 子请求:协议也是 HTTP/1.0,但 $request_method 与主请求一致(如主请求是 POST,镜像子请求也是 POST),$http_user_agent 同样为空,关键看 mirror 配置的 location 是否有唯一标识(比如加个 add_header X-Mirror "true"
  • 二者都不会携带原始请求头(如 AuthorizationCookie),除非显式用 proxy_pass_request_headers on 等透传

rewrite last 和 internal 重定向如何确认执行链路?

rewrite ... last 触发的重写,本质是内部发起新子请求重新匹配 location,不是 HTTP 301/302 跳转。验证方式很直接:

  • 主请求日志中 $status 是最终响应码(如 200),但 $request 仍是原始路径(如 "GET /old/test HTTP/1.1"
  • 对应子请求日志中 $subrequest="1"$request 是重写后路径(如 "GET /new/test HTTP/1.0"),注意协议降级为 HTTP/1.0
  • 务必配合 $request_id 使用 —— 它在主请求和所有衍生子请求中保持一致,是唯一能串起整条执行链的字段
  • 如果发现子请求没有 $request_id,说明你没在 http 块中配置 random_index on 或类似生成逻辑(实际应使用 map + perl / lua 或官方推荐的 ngx_http_core_module 内置支持)

真正难的是跨多个模块嵌套时的归因:比如 SSI 里又 include 了一个带 mirror 的 location,再在里面做 rewrite last。这时单靠 $subrequest 不够,得靠 $request_id + 自定义 header(如 X-Stage: ssi / X-Stage: mirror)打标,否则日志里全是 subreq="1",根本分不清谁是谁。

本文共计923个文字,预计阅读时间需要4分钟。

如何深度追踪并分析SSI、镜像流量及内部重定向的子请求执行细节?

plaintextlog_subrequest 仅在定义了能识别子请求的日志变量(如 $subrequest)且日志格式正确引用时才生效。未配置 log_format 或 access_log 指令,log_subrequest on 等同于未设置。

为什么 $subrequest 总是空?

这是最常卡住人的点:$subrequest 变量本身不输出任何内容,它只在子请求上下文中为字符串 "1",主请求中为空字符串(不是 "0",也不是 NULL)。所以如果你在日志里看到 subreq="",那大概率是主请求;看到 subreq="1",才是子请求。

  • 必须启用 log_subrequest on,该指令需放在 httpserverlocation 块中,作用域决定是否生效
  • $subrequest 仅由 ngx_http_core_module 提供,无需额外编译,但 Nginx 版本需 ≥ 1.7.9(2014 年后版本基本都满足)
  • 日志格式中若用了 $subrequest 却没开启 log_subrequest,该字段会始终为空,且不会报错

SSI 包含和 mirror 指令产生的子请求怎么区分?

两者都会触发子请求,但协议头、请求方法和来源路径明显不同,靠 $request + $subrequest + $http_user_agent 组合就能分辨:

  • SSI 子请求:协议固定为 HTTP/1.0$request_methodGET$request_uri 是被 include 的路径(如 /ssi/header.html),$http_user_agent 为空
  • mirror 子请求:协议也是 HTTP/1.0,但 $request_method 与主请求一致(如主请求是 POST,镜像子请求也是 POST),$http_user_agent 同样为空,关键看 mirror 配置的 location 是否有唯一标识(比如加个 add_header X-Mirror "true"
  • 二者都不会携带原始请求头(如 AuthorizationCookie),除非显式用 proxy_pass_request_headers on 等透传

rewrite last 和 internal 重定向如何确认执行链路?

rewrite ... last 触发的重写,本质是内部发起新子请求重新匹配 location,不是 HTTP 301/302 跳转。验证方式很直接:

  • 主请求日志中 $status 是最终响应码(如 200),但 $request 仍是原始路径(如 "GET /old/test HTTP/1.1"
  • 对应子请求日志中 $subrequest="1"$request 是重写后路径(如 "GET /new/test HTTP/1.0"),注意协议降级为 HTTP/1.0
  • 务必配合 $request_id 使用 —— 它在主请求和所有衍生子请求中保持一致,是唯一能串起整条执行链的字段
  • 如果发现子请求没有 $request_id,说明你没在 http 块中配置 random_index on 或类似生成逻辑(实际应使用 map + perl / lua 或官方推荐的 ngx_http_core_module 内置支持)

真正难的是跨多个模块嵌套时的归因:比如 SSI 里又 include 了一个带 mirror 的 location,再在里面做 rewrite last。这时单靠 $subrequest 不够,得靠 $request_id + 自定义 header(如 X-Stage: ssi / X-Stage: mirror)打标,否则日志里全是 subreq="1",根本分不清谁是谁。