根本原因是ThinkPHP默认不信任任何代理头,request()->ip()只读$_SERVER['REMOTE_ADDR'],而该值在负载均衡或CDN后变为上一跳IP;必须同步配置Nginx的set_real_ip_from与PHP的trusted_proxies,并指定real_ip_header X-Real-IP才能正确解析真实客户端IP。

ThinkPHP 在负载均衡或 CDN 后无法获取真实 IP,根本原因不是框架“坏了”,而是它默认不信任任何代理头——request()->ip() 只读 $_SERVER['REMOTE_ADDR'],而这个值在 Nginx / SLB / CDN 后已经是上一跳的 IP(比如阿里云 SLB 的内网 IP)。
为什么 request()->ip() 返回的是 SLB 或 CDN 的 IP?
ThinkPHP 5.1+ 虽然支持可信代理配置,但默认 trusted_proxies 为空数组,等同于完全忽略 X-Forwarded-For 和 X-Real-IP。哪怕 Nginx 正确透传了这些头,框架也不会去解析。
常见错误现象:
- 本地直连时 IP 正确,上到阿里云 SLB 后所有请求 IP 变成类似
100.64.0.1或192.168.10.5 - CDN 开启后,
request()->ip()固定返回 CDN 节点出口 IP,而非用户真实出口 - 日志里看到大量重复的“内网段 IP”,但用户实际分布在全国各地
关键点:框架行为和 Nginx 配置必须对齐。只改 PHP 不配 Nginx,或只配 Nginx 不设可信段,都无效。
立即学习“PHP免费学习笔记(深入)”;
如何正确配置 Nginx 透传真实 IP 头
Nginx 必须把客户端真实 IP 写入标准头字段,并确保不重复追加、不污染字段。否则 ThinkPHP 解析时会取到伪造或混乱的值。
推荐写法(放在 location 或 server 块中):
set_real_ip_from 10.0.0.0/8; set_real_ip_from 192.168.0.0/16; set_real_ip_from 172.16.0.0/12; set_real_ip_from 127.0.0.1; set_real_ip_from ::1; real_ip_header X-Real-IP; real_ip_recursive on;
说明:
-
set_real_ip_from必须和 PHP 中配置的可信代理段严格一致(如阿里云 SLB 回源段是100.64.0.0/10,就得加进去) -
real_ip_header X-Real-IP表示用哪个头作为“权威真实 IP”,比X-Forwarded-For更可靠(后者可被客户端伪造) -
real_ip_recursive on允许多层代理递归解析,避免只取第一跳 - 不要手动用
proxy_set_header X-Forwarded-For $remote_addr—— 这会覆盖原始值,导致丢失最左真实 IP
ThinkPHP 中显式启用可信代理并封装安全获取逻辑
直接改框架配置文件(config/app.php)是最简方式,但仅适用于单一部署环境;更稳妥的做法是封装独立方法,显式控制校验流程。
在配置中启用(适用于明确知道所有代理段的场景):
// config/app.php
'trusted_proxies' => [
'10.0.0.0/8',
'192.168.0.0/16',
'172.16.0.0/12',
'100.64.0.0/10', // 阿里云 SLB
'100.125.0.0/16', // 腾讯云 CLB
],
然后调用 request()->ip() 才会自动解析 X-Real-IP(前提是 Nginx 已正确设置 real_ip_header)。
但注意:框架默认仍优先取 X-Forwarded-For 最左 IP,且不做私有地址过滤。所以生产建议自己封装:
- 先校验请求来源 IP 是否属于可信段(用
isTrustedProxy($ip, $trustedProxies)) - 若可信,再取
X-Real-IP;否则 fallback 到X-Forwarded-For并过滤掉私有/保留地址(如127.0.0.1、0.0.0.0、10.0.0.0/8) - 永远不用
$_SERVER['HTTP_CLIENT_IP']—— 它未被标准化,多数 CDN/SLB 根本不设该头
最容易被忽略的三个细节
很多团队调了几天没效果,卡在这几个地方:
- Nginx 的
set_real_ip_from段没包含你实际收到请求的源 IP(比如漏了云厂商的特定回源段),导致real_ip_header完全不生效 - ThinkPHP 配置了
trusted_proxies,但 Nginx 没开real_ip_header,或者写了X-Forwarded-For却让框架去信X-Real-IP,头字段和解析逻辑错位 - 测试时用
curl -H "X-Real-IP: 1.2.3.4" http://your-site.com直接发请求 —— 这种伪造头在生产 Nginx + SLB 链路里根本不会被接受,因为来源 IP 不在可信段内,会被直接丢弃
真实链路中,IP 校验是双向的:Nginx 看来源 IP 是否可信,PHP 看头字段是否来自可信段。缺一不可,且段落必须精确匹配。



















