需同时配置nginx realip模块、透传代理头、thinkphp可信代理段,并封装独立ip获取方法,确保request()->ip()返回真实客户端ip。

如果您在Nginx反向代理环境下使用ThinkPHP,发现request()->ip()返回的是代理IP而非用户真实IP,则问题根源在于Nginx未正确解析并注入真实客户端IP,且ThinkPHP未配置可信代理段。以下是解决此问题的步骤:
一、配置Nginx RealIP模块参数
Nginx需启用realip模块并明确声明可信代理来源及头字段,使$remote_addr在进入PHP前已被替换为真实IP,从而让ThinkPHP可直接信任$_SERVER['REMOTE_ADDR']。
1、确认Nginx已编译--with-http_realip_module(主流发行版默认启用)。
2、在http或server块中添加可信代理网段,例如部署于内网Nginx+CDN混合场景:
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from 127.0.0.1;
3、指定从HTTP头提取真实IP的字段,推荐使用X-Forwarded-For(兼容CDN通用):
real_ip_header X-Forwarded-For;
4、启用递归解析,确保跳过所有可信代理IP后取最左非可信IP:
real_ip_recursive on;
5、重载Nginx配置生效:nginx -s reload。
二、补充Nginx代理头透传设置
仅启用RealIP模块不足以覆盖所有代理链场景;必须确保上游代理(如CDN、SLB)将真实IP写入指定头,并由Nginx完整透传至PHP-FPM,否则ThinkPHP无法读取原始头信息进行校验。
1、在location块中添加以下三行关键透传指令:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
2、若使用PHP-FPM,还需在fastcgi_param中显式注入X-Real-IP变量,避免因FPM过滤下划线头导致$_SERVER['HTTP_X_REAL_IP']为空:
fastcgi_param HTTP_X_REAL_IP $remote_addr;
3、检查Nginx是否禁用下划线头:underscores_in_headers off;(若为on需改为off,否则含下划线的头可能被丢弃)。
三、配置ThinkPHP可信代理段
ThinkPHP 5.1+支持手动配置trust_proxy参数,用于校验代理头来源合法性;若不配置,默认不信任任何代理头,request()->ip()将始终回退到REMOTE_ADDR(即未经RealIP修正的上一跳IP)。
1、在config/app.php中添加或修改trust_proxy配置项:
'trust_proxy' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '127.0.0.1'],
2、同步配置proxy_server_ip_header,指定优先读取的HTTP头顺序:
'proxy_server_ip_header' => ['X-Real-IP', 'X-Forwarded-For'],
3、确保proxy_server_ip与Nginx中set_real_ip_from所列网段严格一致,否则校验失败,代理头被忽略。
四、封装独立IP获取方法
依赖框架默认request()->ip()存在逻辑耦合风险;推荐在应用层封装显式控制流程的方法,规避配置遗漏或版本差异影响。
1、在app/common.php或助手函数中定义getClientIp():
function getClientIp() {
$request = \think\Request::instance();
$ip = $request->server('remote_addr', '');
$xRealIp = $request->header('x-real-ip', '');
$xForwardedFor = $request->header('x-forwarded-for', '');
$trustedProxies = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '127.0.0.1'];
$clientIp = '';
if (!empty($xRealIp) && isTrustedProxy($ip, $trustedProxies)) {
$clientIp = $xRealIp;
}
if (empty($clientIp) && !empty($xForwardedFor)) {
$ips = array_map('trim', explode(',', $xForwardedFor));
foreach ($ips as $candidate) {
if (isTrustedProxy($ip, $trustedProxies) && filter_var($candidate, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$clientIp = $candidate;
break;
}
}
}
return $clientIp ?: '0.0.0.0';
}
2、辅助函数isTrustedProxy()需实现CIDR匹配逻辑,验证$ip是否属于任一可信网段。
3、调用时直接使用getClientIp(),不再依赖request()->ip()。
五、验证配置是否生效
最终效果应满足:Nginx日志中$remote_addr为公网IP、PHP中$_SERVER['REMOTE_ADDR']为同一公网IP、ThinkPHP中request()->ip()亦返回该公网IP。
1、在Web根目录新建test-ip.php,内容为:
php var_dump($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_REAL_IP'] ?? null, $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null); ?>
2、访问该页面,观察输出结果:$_SERVER['REMOTE_ADDR']必须显示为用户真实公网IP,且非127.0.0.1或内网地址。
3、在ThinkPHP控制器中执行dump(request()->ip());,确认输出与上述一致。
4、若仍返回代理IP,检查set_real_ip_from是否覆盖实际代理出口段(如阿里云SLB使用100.64.0.0/10,需补入)。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











