web学习笔记编程技术学习与分享

反向代理与真实 IP 的传递

2026 年 08 月 04 日约 3 分钟阅读 运维与部署

上了反向代理或 CDN 后,应用里 $remote_addr 变成了代理的 IP。日志、限流、封禁全部失真。正确取回真实 IP 需要理解转发头的语义。

X-Forwarded-For 是追加,不是覆盖

X-Forwarded-For: 客户端IP, 代理1, 代理2

每一跳把自己的来源追加到链尾。这带来一个关键问题:客户端可以伪造第一段。攻击者直接发一个带 X-Forwarded-For: 1.2.3.4 的请求,代理会原样追加,应用若取第一段,拿到的是伪造值。

正确姿势:从右往左数

信任链是固定的:你知道有几个可信代理,就从右往左数第 N 个。例如前置 CDN + 本机 Nginx,则真实 IP 是倒数第二个:

# Nginx:信任 CDN 网段,realip 模块自动改写 $remote_addr
set_real_ip_from 173.245.48.0/20;   # CDN 网段
real_ip_header X-Forwarded-For;
real_ip_recursive on;

应用层直接取 $remote_addr,不需要自己解析头。永远不要无条件信任 XFF 第一段——它等价于把 IP 来源控制权交给客户端。

传递头的选择

  • X-Forwarded-For:事实标准,链式;
  • X-Real-IP:Nginx 常用的单值头,由最外层代理设置;
  • 标准化方案是 Forwarded 头(RFC 7239),语法严谨但生态支持一般。
location / {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://backend;
}

框架层

多数框架有「受信任代理」配置:PHP 下 $ _SERVER['REMOTE_ADDR'] 与 HTTP_X_FORWARDED_FOR 的取舍、Express 的 trust proxy、Laravel 的 trustedproxies 包。配置的网段必须与实际部署一致,配宽松了等于信任任意客户端。

排查方法

在每层记录收到的头与连接来源,对同一请求串起来看:

log_format trace '$remote_addr | XFF: $http_x_forwarded_for | $http_x_real_ip';

大多数「限流把所有用户算成同一个 IP」「封禁封不住」的问题,答案都在这条日志里。

Nginx反向代理安全