$_SERVER['REMOTE_ADDR']总是CDN的IP,因CDN作为反向代理与PHP建立连接;真实IP在X-Forwarded-For等头中,需结合CDN白名单校验并过滤私有地址后提取首个合法公网IP。

PHP 中 $_SERVER['REMOTE_ADDR'] 为什么总是 CDN 的 IP?
因为 CDN 作为反向代理,实际与 PHP 服务建立 TCP 连接的是 CDN 节点,不是用户浏览器。所以 $_SERVER['REMOTE_ADDR'] 拿到的永远是 CDN 服务器的出口 IP,而非真实访客 IP。
真实 IP 会被 CDN 加在 HTTP 请求头里,常见字段有:X-Forwarded-For、X-Real-IP、X-Forwarded-For(注意:可能被伪造,不可直接信任)。
关键点:不能只读一个头,也不能无条件信任;必须结合 CDN 厂商文档 + 白名单校验。
如何安全地从 X-Forwarded-For 提取真实 IP?
X-Forwarded-For 是逗号分隔的 IP 链,格式类似:"203.123.45.67, 192.168.1.100, 10.0.0.1"。最左边是原始客户端 IP,但可被恶意构造;最右边是上一跳代理 IP(即 CDN 节点),相对可信。
立即学习“PHP免费学习笔记(深入)”;
安全做法是:只接受来自已知 CDN IP 段的请求,并从中提取第一个非私有、非保留地址:
- 先检查
$_SERVER['HTTP_X_FORWARDED_FOR']是否存在且非空 - 用
explode(', ', ...)拆分,从左到右遍历每个 IP - 跳过所有私有地址(
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8、::1等)和无效格式 - 返回第一个合法公网 IPv4 或 IPv6
示例片段(不依赖框架):
$ip = $_SERVER['REMOTE_ADDR'];
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
foreach ($ips as $candidate) {
if (filter_var($candidate, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$ip = $candidate;
break;
}
}
}不同 CDN 对应的可信头字段和白名单怎么查?
没有统一标准,必须按你用的 CDN 查官方文档:
- Cloudflare:用
HTTP_CF_CONNECTING_IP(最可靠),同时需校验$_SERVER['HTTP_CF_RAY']是否存在,或限制来源 IP 段为 Cloudflare 官方 IP 列表 - 阿里云 CDN / 全站加速:优先用
HTTP_X_REAL_IP,并验证请求来源是否在 阿里云 CDN 回源 IP 段 内 - 腾讯云 CDN:推荐
HTTP_X_REAL_IP,白名单参考 腾讯云 CDN 回源 IP 文档 - 自建 Nginx + CDN:确保 CDN 配置了
proxy_set_header X-Real-IP $remote_addr;,后端再读HTTP_X_REAL_IP
⚠️ 忽略白名单校验等于开放 IP 伪造入口——攻击者可手动加 X-Forwarded-For: 1.2.3.4 绕过限流或日志统计。
为什么用 $_SERVER['REMOTE_ADDR'] 校验 CDN 来源比读 Header 更底层?
因为 $_SERVER['REMOTE_ADDR'] 是 TCP 连接的真实对端 IP,无法被 HTTP 头伪造。只要你在 Web 服务器(如 Nginx)或 PHP-FPM 层做了来源 IP 白名单限制,就能确保后续读取的任何 X-* 头都来自可信 CDN 节点。
典型 Nginx 配置节选(仅允许 Cloudflare IP 访问):
set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; # ...其他 Cloudflare IP 段 real_ip_header X-Forwarded-For; real_ip_recursive on;
这样配置后,Nginx 会自动把 $_SERVER['REMOTE_ADDR'] 替换为 X-Forwarded-For 中最右合法 IP,PHP 就可以直接用 $_SERVER['REMOTE_ADDR']——但前提是确认你没在 PHP 层重复解析,否则反而出错。
真正容易被忽略的点:CDN 厂商可能动态更新 IP 段,白名单需定期同步;而很多项目写死判断逻辑,上线后半年没更新,某天突然发现日志 IP 全乱了。











