可通过 $upstream_addr 变量间接记录 ip_hash 调度到的后端节点 ip,该变量在 proxy_pass 执行后自动填充实际转发地址(如 192.168.1.10:8080),需配合 log_format 使用;若前端有代理,须配置 set_real_ip_from 等指令确保 $remote_addr 为真实客户端 ip,否则调度与日志失真。

直接在日志中记录用户被 ip_hash 调度到的具体后端节点 IP,Nginx 原生不支持——因为 ip_hash 是运行时动态计算、内部路由决策,其结果不会暴露为变量。但可以通过合理组合 Nginx 模块与变量机制,**间接、可靠地实现该目标**。
用 $upstream_addr 记录实际转发的后端地址
这是最常用且无需额外模块的方法。只要请求成功转发(非 502/503 等错误),$upstream_addr 就会自动记录最终选中的后端服务器地址(含 IP 和端口)。
- 它在
proxy_pass执行后填充,适用于所有负载均衡策略(包括ip_hash) - 值格式示例:
192.168.1.10:8080,若发生重试则可能为多个地址逗号分隔 - 需搭配
log_format使用,例如:
注意:$upstream_addr 在 upstream 失败或未触发 proxy_pass(如静态响应、rewrite 跳转)时为空,日志中显示为“-”。
确保真实客户端 IP 参与哈希,避免日志失真
如果前端有 CDN 或代理,$remote_addr 是代理 IP,ip_hash 会基于它计算,导致所有用户映射到同一后端——此时 $upstream_addr 虽然能记,但反映的是错误的调度逻辑。
- 必须先正确提取真实 IP:使用
set_real_ip_from+real_ip_header X-Forwarded-For+real_ip_recursive on - 启用后,
$remote_addr自动变为真实客户端 IP,ip_hash才真正生效,$upstream_addr记录的才是“该用户被分配到的节点” - 可额外在日志中加入
$http_x_forwarded_for或自定义变量(如$clientRealIP)用于交叉核对
进阶:用 Lua 获取更精细的调度信息
若需在日志中记录“本应选哪个节点但因失败重试了哪个”,或想绕过 $upstream_addr 的重试模糊性,可用 ngx_http_lua_module 在 proxy_pass 前主动计算并缓存目标节点。
- 在 upstream 块外,用
map或set_by_lua_block预计算哈希索引(需已知 server 列表顺序和数量) - 将索引映射为对应 server 地址,存入自定义变量(如
$chosen_backend) - 该变量不受重试影响,稳定反映 ip_hash 的原始决策,适合审计或调试
- 缺点:需维护 server 列表一致性;扩容/缩容时需同步更新 Lua 逻辑或改用共享字典缓存
不推荐:尝试解析 ip_hash 内部哈希值
Nginx 源码中 ip_hash 使用固定算法(前三个字节循环哈希,模数为当前存活节点数),但该过程不可导出,且依赖运行时存活节点状态(down 标记、健康检查结果等)。硬编码模拟不仅易错,还会在节点变更、故障剔除时立即失效,反而误导日志分析。











