关键在于首层代理设X-Real-IP为$remote_addr,中间层透传不覆盖,后端优先信X-Real-IP但需校验;更稳妥是Nginx用realip模块归一化REMOTE_ADDR。
关键不在“设不设”X-Real-IP,而在于“谁来设、怎么设、设给谁看”。单纯在某一层Nginx里写一句
,反而会覆盖掉上游传来的真正客户端IP,导致后端拿到的是中间代理的IP。
首层代理必须用 $remote_addr 设置 X-Real-IP
只有最靠近客户端的那一层代理(比如CDN或WAF)能直接看到真实IP。它应将自身连接的客户端地址写入X-Real-IP:
同时务必设置
或
这一步不可被后续代理覆盖或重写
中间层代理必须透传而非重写 X-Real-IP
第二层及之后的Nginx(如业务入口层)不能用自己的 $remote_addr 覆盖X-Real-IP,否则就丢了源头信息:
正确做法是:只传递上游已有的值:
同时继续追加自身IP到X-Forwarded-For:
若上游没传X-Real-IP,该字段为空,后端需降级使用X-Forwarded-For首段
后端服务应优先信任 X-Real-IP,但需校验来源可信性
应用代码中直接读
最简洁,但前提是整个链路可控:
PHP:
Python:
注意:如果请求可能绕过首层代理(如内网直连),X-Real-IP 可能被伪造,建议结合白名单或 realip 模块做服务端校验
更稳妥的做法:Nginx 层统一提取并覆盖 $remote_addr
与其依赖后端解析,不如让Nginx自己完成“归一化”:
启用
配置可信代理段:
、
指定头字段:
或
开启递归:
之后所有日志和FastCGI变量(如
)自动使用真实IP
proxy_set_header X-Real-IP $remote_addrproxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $remote_addr;$proxy_add_x_forwarded_forproxy_set_header X-Real-IP $http_x_real_ip;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;X-Real-IP$_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR']request.headers.get('X-Real-IP') or request.remote_addrhttp_realip_moduleset_real_ip_from 10.0.0.0/8;set_real_ip_from 192.168.0.0/16;real_ip_header X-Real-IP;real_ip_header X-Forwarded-For;real_ip_recursive on;fastcgi_param REMOTE_ADDR $remote_addr