根本原因是nginx未配置set_real_ip_from信任cdn回源ip段,导致不解析x-forwarded-for头,$remote_addr仍为cdn节点ip;必须显式声明可信ip段、设置real_ip_header和real_ip_recursive on,并确保fastcgi_param remote_addr $remote_addr透传生效。

宝塔面板无法获取客户端真实 IP,根本原因不是 Nginx 没“读”X-Forwarded-For,而是它默认不信任、不解析这个头——CDN 回源流量进来后,$remote_addr 仍是 CDN 节点的 IP,PHP 的 $_SERVER['REMOTE_ADDR'] 自然拿不到真实用户 IP。
CDN 回源 IP 段必须显式声明才可信
Nginx 的 set_real_ip_from 不是可选项,是安全强制项。没配这个,real_ip_header X-Forwarded-For 就完全不生效,哪怕头存在、格式正确也视而不见。
- Cloudflare 官方回源段必须用 IPv4 + IPv6 全列表,不能只填
173.245.48.0/20这类旧子网 - 阿里云 CDN 在控制台「域名管理 → 回源 IP 段」里查,不是“加速区域 IP”,且每季度可能更新
- 腾讯云 CDN 叫「IP 回传白名单」,需在「访问控制 → 回源设置」中开启并确认段已填全
- 多个段要逐行写,不能合并成一条;顺序无关,但漏一条就可能导致某批请求 fallback 到 CDN IP
real_ip_recursive on 是关键开关,不是可有可无
CDN(尤其是多层代理或 WAF 后接 CDN)常把 X-Forwarded-For 拼成 用户IP, CDN节点1, CDN节点2。关掉 real_ip_recursive,Nginx 默认取最左(即用户 IP),看似合理——但一旦中间有不可信代理伪造了左边字段,就彻底失效。
-
real_ip_recursive on表示:从右往左遍历,跳过所有匹配set_real_ip_from的 IP,取第一个不匹配的——这才是真实用户 IP - 关掉它(或不写)时,Nginx 直接取最左值,等于放弃校验,等同于信任任意来源的
X-Forwarded-For - 宝塔配置文件里必须紧挨着
set_real_ip_from和real_ip_header写,三者缺一不可,且必须在server {块最上方、任何location之前
日志和 PHP 中的 REMOTE_ADDR 不会自动同步,需验证是否生效
改完 Nginx 配置只是第一步。Nginx 修改的是 $remote_addr 变量,但 PHP 能否拿到,取决于 FastCGI 参数有没有把新值透传过去。
- 检查站点配置文件里是否存在
fastcgi_param REMOTE_ADDR $remote_addr;—— 宝塔默认有,但如果手动改过或套了自定义模板,可能被删或写错成$http_x_forwarded_for - 加个临时调试页:
<?php var_dump($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'none');,输出应为「真实IP」+「一串逗号分隔 IP」 - 如果
REMOTE_ADDR仍是 CDN 段(如100.100.x.x),说明set_real_ip_from段没覆盖到当前回源 IP,或real_ip_header拼写错误(比如写成X-Forwarded-Forr) - 日志格式中的
$remote_addr会自动更新,无需额外改log_format,但旧日志仍存 CDN IP,新请求才生效
最容易被忽略的是:CDN 控制台侧是否真正开启了「传递客户端真实 IP」。Nginx 配得再准,CDN 没发 X-Forwarded-For 头,一切归零。上线前务必用 curl -H "Host: yoursite.com" http://your-server-ip 直连服务器,看响应头里有没有那个头——没有,就先别调 Nginx。











