PHP获取客户端真实IP需先校验REMOTE_ADDR是否在可信代理列表,再从HTTP_X_REAL_IP或HTTP_X_FORWARDED_FOR提取并验证IP;直接信任伪造头将导致安全风险。

PHP 获取客户端真实 IP 并不简单,$_SERVER['REMOTE_ADDR'] 只能拿到直连服务器的地址(比如反向代理或 CDN 后就是它自己的出口 IP),真要拿到用户浏览器发起请求时的原始 IP,得结合多个 $_SERVER 字段判断,并做可信校验。
为什么 $_SERVER['REMOTE_ADDR'] 不可靠
当网站接入 Nginx、Apache 代理,或使用 Cloudflare、阿里云 CDN、腾讯云 WAF 等服务后,PHP 接收到的 $_SERVER['REMOTE_ADDR'] 是上游代理的 IP,不是用户真实出口 IP。此时必须依赖代理透传的 HTTP 头,如 X-Forwarded-For 或 X-Real-IP。
但这些头可被客户端伪造,直接信任会导致安全风险(比如伪造管理员 IP 绕过限制)。所以关键不是“取哪个字段”,而是“在哪些可信环境下才取它”。
如何安全地获取真实客户端 IP
核心逻辑:只信任你明确配置了反向代理的 IP 段,且仅从该代理传来的头中提取 IP。其他来源一律忽略。
立即学习“PHP免费学习笔记(深入)”;
- 先定义你信任的代理 IP 列表(如内网网段
127.0.0.1、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16,或具体 CDN 回源 IP 段) - 检查
$_SERVER['REMOTE_ADDR']是否在可信代理列表中 - 如果是,再解析
$_SERVER['HTTP_X_FORWARDED_FOR'](逗号分隔,最左边是原始客户端 IP)或$_SERVER['HTTP_X_REAL_IP'] - 对解析出的 IP 做基础校验(是否为合法 IPv4/IPv6、是否私有地址等,视业务需求而定)
示例代码:
function getClientIp(): ?string
{
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
$trustedProxies = ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
// 判断 REMOTE_ADDR 是否来自可信代理
$isTrusted = false;
foreach ($trustedProxies as $proxy) {
if (strpos($proxy, '/') !== false) {
[$net, $mask] = explode('/', $proxy);
$ipLong = ip2long($remoteAddr);
$netLong = ip2long($net);
if (($ipLong & ~((1 << (32 - (int)$mask)) - 1)) === $netLong) {
$isTrusted = true;
break;
}
} elseif ($remoteAddr === $proxy) {
$isTrusted = true;
break;
}
}
if (!$isTrusted) {
return $remoteAddr; // 直连用户,直接返回
}
// 从可信代理头中取真实 IP
$ip = $_SERVER['HTTP_X_REAL_IP'] ?? '';
if (!$ip && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
$ip = $ips[0] ?? '';
}
// 基础校验:只接受合法公网 IPv4(可按需扩展 IPv6 或放开私有地址)
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
return $ip;
}
return $remoteAddr;}
常见错误与绕过场景
很多网上示例直接取 $_SERVER['HTTP_X_FORWARDED_FOR'] 第一个值,这等于把 IP 校验权交给了客户端 —— 攻击者只要发个 X-Forwarded-For: 1.2.3.4 就能伪造 IP。
- 没做代理 IP 白名单校验 → 任意请求都能伪造
X-Forwarded-For - 用
$_SERVER['HTTP_X_FORWARDED_FOR']全串未分割 → 可能取到代理链中间 IP,而非最左原始 IP - 没过滤空格或换行 → 可能被注入恶意头(如
X-Forwarded-For: 1.2.3.4\r\nX-Forwarded-For: 5.6.7.8) - 忽略 IPv6 场景 →
filter_var默认不校验 IPv6,需加FILTER_FLAG_IPV6
不同部署环境对应的关键头和配置
你得知道你的流量路径上谁在转发、谁在加头,否则白写逻辑:
- Nginx 作为反向代理:需配置
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - Cloudflare:真实 IP 在
$_SERVER['HTTP_CF_CONNECTING_IP'],且只在启用“True Client IP”功能并配置了源站 IP 白名单后才可信 - 阿里云 SLB / 腾讯云 CLB:一般支持透传
X-Forwarded-For,但需在控制台开启“获取真实 IP”开关 - CDN 回源:务必查文档确认其回源时使用的头字段名(有些用
X-Forwarded-For,有些用X-Real-IP,甚至自定义)
没配对头字段或没开透传,代码再严谨也拿不到真实 IP;配错了头字段,就等于在读一个永远为空的变量。











