$_SERVER[‘REMOTE_ADDR’] 在 Nginx + PHP-FPM 下不等于真实客户端 IP,是因为 Nginx 默认以代理身份转发请求,使 REMOTE_ADDR 显示自身地址(如 127.0.0.1),而真实 IP 需通过 real_ip 模块配合 proxy_set_header 和 set_real_ip_from 等配置从可信 HTTP 头(如 X-Real-IP)提取并验证。
为什么 $_SERVER['REMOTE_ADDR'] 在 Nginx + PHP-FPM 下不等于真实客户端 IP
因为 Nginx 默认不会把原始请求的 IP 透传给后端 PHP-FPM,而是把自身作为代理,把连接来源(比如本机 127.0.0.1)当作 REMOTE_ADDR。真实 IP 被压在 HTTP 头里(如 X-Forwarded-For 或 X-Real-IP),PHP 默认不读这些头。
常见现象:$_SERVER['REMOTE_ADDR'] 永远是 127.0.0.1 或 ::1;$_SERVER['HTTP_X_FORWARDED_FOR'] 可能为空或为逗号分隔字符串(含代理链);$_SERVER['HTTP_X_REAL_IP'] 可能未设置。
- Nginx 必须显式用
proxy_set_header把真实 IP 注入请求头 - PHP 不会自动解析或覆盖
REMOTE_ADDR,需自行从可信头提取并校验 - 不能无条件信任
X-Forwarded-For—— 它可被客户端伪造,只应在 Nginx 确认可信代理后才使用
Nginx 配置:必须设置 proxy_set_header X-Real-IP 和 real_ip 指令
光加 proxy_set_header 不够,Nginx 还得用 real_ip 模块识别并替换 $remote_addr,否则 PHP-FPM 收到的仍是 Nginx 的连接地址。
确保 Nginx 编译时启用了 --with-http_realip_module(主流发行版包通常已启用)。
- 在
location ~ \.php$块内添加:proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $remote_addr; - 在
http或server块中添加:set_real_ip_from 127.0.0.1;(若 PHP-FPM 与 Nginx 同机)real_ip_header X-Real-IP;(指定用哪个头还原$remote_addr) - 如果前端还有 Cloudflare、CDN 或负载均衡器,要把它们的出口 IP 加入
set_real_ip_from,并改用X-Forwarded-For或X-Forwarded-For最右非私有 IP(需配合real_ip_recursive on;)
PHP 中安全获取客户端 IP 的推荐写法
不要直接用 $_SERVER['HTTP_X_FORWARDED_FOR'] 或 $_SERVER['HTTP_X_REAL_IP'],必须结合 Nginx 的可信代理配置做白名单判断。