解决xff伪造的关键是只信任直接上游代理ip:thinkphp需严格配置trust_proxies为内网可信段,nginx对外层设xff为$remote_addr、内层用$proxy_add_x_forwarded_for,并启用realip模块校验,应用层统一读取修正后的$remote_addr。

多层反向代理下 X-Forwarded-For(XFF)被伪造,本质是信任链断裂:每一层代理都可能追加或篡改该头,而最终应用若不校验来源可信性,就会把攻击者伪造的 IP 当作真实客户端地址。解决的关键不是“怎么取 XFF”,而是“只从可信源头解析它”。
明确可信代理边界,只认最靠近应用的那一跳
无论前面有 CDN、SLB 还是多级 Nginx,ThinkPHP 或 Nginx 只应信任直接和它建立 TCP 连接的上一级代理 IP(即 $remote_addr 的值)。这个 IP 必须落在你手动配置的白名单内,才允许解析其携带的 XFF 头。
- 在 ThinkPHP 6.1+ 中,严格填写 config/request.php 的
trust_proxies,仅填 Nginx 所在机器的内网 IP 或网段(如'10.0.0.0/8'),绝不填公网 IP 或 CDN 域名 - 若用阿里云 SLB,填的是 SLB 实际打到 ECS 的源 IP 段(查 ECS 日志里的
$remote_addr),不是控制台显示的 VIP - 填错会导致所有请求 fallback 到
$_SERVER['REMOTE_ADDR'],表现为全量 IP 变成 127.0.0.1 或统一内网段
Nginx 层必须做源头净化,不传不可信 XFF
最外层对外的 Nginx(接 CDN/公网)要主动覆盖 XFF,确保后端永远只收到一个干净、可信的值;中间层 Nginx 则负责正确拼接,不引入污染。
- 对外入口 Nginx(如直连 Cloudflare 或用户浏览器):
proxy_set_header X-Forwarded-For $remote_addr; - 内网中间层 Nginx(如 Nginx → PHP-FPM):
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 同时启用 realip 模块:
set_real_ip_from设为上游可信代理段,real_ip_header X-Forwarded-For,real_ip_recursive on
应用层放弃“自动降级”逻辑,统一走修正后地址
不要依赖 Request::ip() 的模糊 fallback 行为——它是否降级,完全取决于你有没有配对 trust_proxies。一旦配错,业务就不可控。
- 在控制器中,优先使用
request()->ip(),但前提是已正确配置trust_proxies - 更稳妥的做法:直接读取 Nginx 通过
realip模块修正后的$remote_addr(即$_SERVER['REMOTE_ADDR']已被覆盖为真实客户端 IP) - 彻底禁用对原始
HTTP_X_FORWARDED_FOR的直接读取,避免绕过框架校验
验证是否生效的两个硬指标
配置完成后,用 Postman 伪造 X-Forwarded-For: 1.1.1.1, 2.2.2.2 发起请求,观察结果:
- 日志或调试输出中,
Request::ip()返回的必须是真实发起请求的客户端 IP(如你本地出口 IP),而非伪造值 - 若请求来自非白名单 IP(比如直接 curl 你的服务器),即使带了 XFF 头,也必须返回
$_SERVER['REMOTE_ADDR'](即那个不可信来源的 IP),而不是取 XFF 第一段











