ThinkPHP 的 request()->ip() 经常不准,是因为默认只信任 127.0.0.1 和 ::1 作为代理,未配置 trusted_proxies 时会将 Nginx 或 SLB 的内网 IP(如 10.x.x.x)误认为真实客户端 IP;必须显式配置可信代理网段、Nginx 正确设置 X-Real-IP,并禁用不可信的 X-Forwarded-For fallback。

直接用 $_SERVER['REMOTE_ADDR'] 拿到的几乎肯定是反向代理或负载均衡的内网 IP,不是真实调用方地址。必须结合可信代理配置 + 正确的 header 解析逻辑,否则统计结果完全不可信。
ThinkPHP 的 request()->ip() 为什么经常不准
ThinkPHP 5.1+ 的 request()->ip() 默认只信任 127.0.0.1 和 ::1 这两个地址作为代理,线上部署时若没配 trusted_proxies,它就会把 Nginx 或 SLB 转发过来的 10.x.x.x 地址当成本地请求 IP 返回。
- 在
config/app.php中显式添加内网段:'trusted_proxies' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'](按你实际的 VPC/IDC 网段填) - Nginx 配置里必须有
proxy_set_header X-Real-IP $remote_addr;,且不能让前端透传伪造的X-Forwarded-For - 禁用无条件 fallback 到
X-Forwarded-For:该字段可被客户端随意设置,直接取会导致来源污染
用中间件统一提取并标记来源信息
别在每个控制器里重复写 $request->header(),所有来源字段(IP、UA、Referer、自定义头)应在中间件里一次性提取、校验、组合成 source_tag,再挂载到 request 对象上供后续使用。
- 新建中间件
app/middleware/TrackSource.php - 在
handle()中调用$request->ip()(已配好trusted_proxies的前提下)获取真实 IP - 用
$request->header('user-agent')判断是否为MiniProgram、okhttp、curl等典型客户端 - 检查
$request->header('referer')是否为空或来自白名单域名(防 Referer 注入) - 若存在
X-App-ID或X-Client-Type,校验其格式(如 UUID 或预设枚举值),再拼入 tag - 最终存为
$request->withAttr('source_tag', 'wxapp@10.20.30.40')
接口身份校验不能依赖 session
第三方调用方没有 cookie,think\Session 或 session_id() 在接口场景下纯属误用——既无法识别真实调用方,又容易因复用 session 导致来源混淆。
立即学习“PHP免费学习笔记(深入)”;
- API 身份凭证只能是三类:长期有效的
API Key(Header 传X-API-Key)、短期有效的JWT(Authorization: Bearer xxx)、或标准OAuth2 Access Token - 校验通过后,把调用方标识(如
app_id)也写进source_tag,例如wxapp-app_abc123@10.20.30.40 - 日志记录时,对公网 IP 做脱敏(如
10.20.30.40 → 10.20.30.*),UA 截断长度(如只存前 128 字符),避免敏感信息泄露
完整中间件源码示例(可直接用)
namespace app\middleware;
use think\Request;
use Closure;
class TrackSource
{
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
$ua = $request->header('user-agent', '');
$referer = $request->header('referer', '');
$appId = $request->header('x-app-id', '');
$clientType = $request->header('x-client-type', '');
// 简单 UA 分类
$client = 'unknown';
if (stripos($ua, 'miniprogram') !== false) {
$client = 'wxapp';
} elseif (stripos($ua, 'okhttp') !== false) {
$client = 'android';
} elseif (stripos($ua, 'curl') !== false) {
$client = 'curl';
} elseif (stripos($ua, 'Mozilla') !== false) {
$client = 'browser';
}
// 组合 source_tag
$tag = $client;
if ($appId && ctype_alnum(str_replace(['-', '_'], '', $appId))) {
$tag .= '-app_' . substr($appId, 0, 8);
}
$tag .= '@' . preg_replace('/\.\d+$/', '.*', $ip);
$request->withAttr('source_tag', $tag);
return $next($request);
}
}
注册方式:在 app/middleware.php 中加入 app\middleware\TrackSource::class 即可生效。注意这个中间件必须放在路由解析之前,且不要被其他中间件提前终止。
最常被忽略的是 trusted_proxies 配置和 Nginx 的 X-Real-IP 设置——两者缺一,$request->ip() 就永远拿不到真实地址,后续所有来源分析都建立在错误基础上。



















