Nginx透传X-Forwarded-For需用$proxy_add_x_forwarded_for确保链路累积,日志直接记录$http_x_forwarded_for完整值,并配合realip模块校验可信代理边界以防御伪造。

要让 Nginx 正确透传并记录完整的 X-Forwarded-For 代理链路,核心不是“转发一个头”,而是确保该头在每一跳都**不被覆盖、不被截断、不被伪造**,同时让日志和后端能安全使用它。关键在配置逻辑和信任边界,不是变量拼写。
用对变量:必须用 $proxy_add_x_forwarded_for
这是最常踩的坑。不能写成:
- proxy_set_header X-Forwarded-For $remote_addr;(会清空上游链路)
- proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $remote_addr";(手动拼接易出空值或格式错)
正确写法只有一条:
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
它的行为是:如果请求已有 X-Forwarded-For 头,就在末尾追加当前 $remote_addr(用逗号+空格分隔);如果没有,就直接设为 $remote_addr。这样链路才能逐层累积,比如:
用户 → CDN → Nginx-A → Nginx-B → 后端
最终头内容为:"203.0.113.45, 198.51.100.1, 10.0.1.5, 10.0.2.8"
确保上游链路未被污染
$proxy_add_x_forwarded_for 再准,也依赖上游传来的原始值。需验证:
- CDN 或负载均衡器是否按规范追加(如 Cloudflare 传真实 IP,AWS ALB 需开启 Preserve Host Header)
- 中间任何一层 Nginx、WAF 或网关都不能执行
proxy_set_header X-Forwarded-For ""或重写该头 - 后端应用(如 PHP/Node.js)禁止再往该头追加本机 IP,否则混入不可信地址
- 临时加一句
add_header X-Debug-XFF "$http_x_forwarded_for";,用 curl 查看响应头,确认原始值完整
在日志中直接记录完整字符串
Nginx 的 $http_x_forwarded_for 变量就是原始请求头的原样映射,不做解析也不截断。只要上游没污染,它天然就是完整链路。
定义日志格式时直接引用即可:
log_format full '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';access_log /var/log/nginx/access.log full;
日志示例:
10.0.2.100 - - [13/Oct/2026:13:45:22 +0800] "GET /api/data HTTP/1.1" 200 324 "-" "curl/7.68.0" "203.0.113.45, 198.51.100.1, 10.0.1.5"
防御伪造:只信任可信代理的链路
客户端可随意发送 X-Forwarded-For: 1.1.1.1, 2.2.2.2。所以完整链路虽记下了,但不能直接用于鉴权或限流——必须结合 realip 模块校验可信边界。
- 在最终接入的 Nginx 中配置可信代理段:
set_real_ip_from 198.51.100.0/24;(CDN)、set_real_ip_from 10.0.0.0/8;(内网 LB) - 指定解析来源:
real_ip_header X-Forwarded-For; - 开启递归:
real_ip_recursive on;(Nginx 从右往左剔除所有可信 IP,取第一个非可信地址作为真实客户端 IP) - 这样
$remote_addr和$http_x_real_ip才真正可信,而$http_x_forwarded_for仍保留全链,供审计与排障











