不能直接用 ngx_http_mirror_module 做生产环境 1:1 无损镜像——它不保证请求体完整转发、不透传原始 Host/Connection 头、不支持异步非阻塞回写,压测集群收到的请求和线上实际流量存在系统性偏差。

不能直接用 ngx_http_mirror_module 做生产环境 1:1 无损镜像——它不保证请求体完整转发、不透传原始 Host/Connection 头、不支持异步非阻塞回写,压测集群收到的请求和线上实际流量存在系统性偏差。
mirror 指令会丢请求体且无法恢复
nginx 的 mirror 指令在读取客户端请求时,默认只读取请求头,不主动读取请求体(除非后续 upstream 显式需要)。镜像子请求发起时,原始请求体已不可见,mirror 子请求发出去的是空 body。
- 修复方式:必须在
location块中显式添加proxy_pass_request_body off;+proxy_buffering off;,并配合proxy_http_version 1.1;和proxy_set_header Content-Length ""; - 但更稳妥的做法是改用
lua-resty-http在 access 阶段手动读取ngx.req.read_body(),再异步 POST 到压测地址——否则 multipart/form-data、JSON 大 body 必丢 - 注意:
mirror不支持设置超时,子请求卡住会拖慢主请求,必须用mirror_request_timeout(需 patch 或升级到 nginx 1.25+)
Host、X-Real-IP 等关键头默认不透传
mirror 发起的子请求使用的是新构建的 request line,所有头字段重置为默认值,原始 Host、X-Forwarded-For、User-Agent 全部丢失。
- 必须显式用
proxy_set_header补全:proxy_set_header Host $host;、proxy_set_header X-Real-IP $remote_addr;、proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 若压测集群依赖
Referer或自定义 header(如X-Trace-ID),必须逐个proxy_set_header,漏一个就导致链路追踪断裂或鉴权失败 - 特别注意:
Connection和Upgrade头不能透传,否则镜像请求可能意外升级成 WebSocket,直接失败
镜像失败不报错,主流程完全无感知
mirror 是“fire-and-forget”模型:子请求返回 4xx/5xx、超时、DNS 失败,nginx 全部静默吞掉,主请求照常处理。你永远不知道镜像是否成功,直到压测数据对不上。
- 没有内置指标暴露镜像成功率,需靠日志 + log_format 拆解
$upstream_status(仅对 mirror 子请求有效) - 推荐加一层 lua,在
log_by_lua_block中调用ngx.ctx.mirror_status(需提前在 rewrite 阶段存入)并上报 Prometheus - 压测集群入口务必开启 access_log,并配置独立日志路径,避免和线上日志混在一起;否则查问题时根本分不清哪条是镜像来的
真正无损镜像的关键不在模块本身,而在你能否控制请求体生命周期、头字段完整性、失败可观测性。用 mirror 只是起点,后面至少要补 lua 读体、自定义日志、独立监控链路——否则所谓“1:1”,只是 URL 和 method 层面的自我安慰。

















