php获取本机ip在负载均衡下不同_php识别lb环境取ip【技巧】

3次阅读

不能直接用$_SERVER[‘REMOTE_ADDR’],因为它返回的是负载均衡器到php服务器的内网IP;应按CF-Connecting-IP→X-Real-IP→X-forwarded-For(取首个非私有IP)→REMOTE_ADDR优先级安全获取真实客户端IP。

php获取本机ip在负载均衡下不同_php识别lb环境取ip【技巧】

PHP 在负载均衡下获取真实客户端 IP 为什么不能直接用 $_SERVER[‘REMOTE_ADDR’]

因为 $_SERVER['REMOTE_ADDR'] 拿到的是 LB(如 nginx、AWS ALB、Cloudflare)到 PHP 应用服务器的连接 IP,通常是内网地址(比如 10.0.1.5127.0.0.1),不是用户真实出口 IP。

LB 一般会把原始请求头(如 X-Forwarded-ForX-Real-IP)透传过来,但必须确认 LB 确实配置了转发,且你的 PHP 服务处于可信内网——否则直接信任这些头会导致 IP 伪造漏洞。

  • 务必只在 LB 和 PHP 之间是私有网络(无公网直连)时才启用头字段解析
  • 不要无条件信任 $_SERVER['HTTP_X_FORWARDED_FOR'],它可能被客户端篡改或含多个逗号分隔的 IP
  • 不同 LB 默认行为不同:Nginx 需手动加 proxy_set_header X-Real-IP $remote_addr;;Cloudflare 用 CF-Connecting-IP;AWS ALB 默认带 X-Forwarded-For

如何安全提取真实客户端 IP(推荐判断链)

按可信度从高到低依次检查,并只取第一个合法 IPv4/ipv6 地址(跳过私有网段和无效格式):

  • 优先用 $_SERVER['HTTP_CF_CONNECTING_IP'](Cloudflare 环境独有,不可伪造)
  • 其次看 $_SERVER['HTTP_X_REAL_IP'](Nginx 显式设置,较可靠)
  • 再 fallback 到 $_SERVER['HTTP_X_FORWARDED_FOR']:取逗号分隔后的**第一个非私有 IP**(如 "203.0.113.1, 192.168.1.10" → 取 203.0.113.1
  • 最后才回退到 $_SERVER['REMOTE_ADDR'](仅作兜底,通常只是 LB 内网地址)

示例逻辑片段(不依赖扩展):

立即学习PHP免费学习笔记(深入)”;

// 注意:需确保运行在可信内网环境 function getRealIP() {     $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';      if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {         $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];     } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {         $ip = $_SERVER['HTTP_X_REAL_IP'];     } elseif (!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_var($candidate, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {                 $ip = $candidate;                 break;             }         }     }      return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0'; }

为什么 filter_var(…, FILTER_FLAG_NO_PRIV_RANGE) 不能省略

很多 LB(尤其是自建 Nginx + proxy_pass)会把 X-Forwarded-For 做追加操作,例如:
用户 → LB → PHP,LB 把原始 IP 加到头里,但若 LB 自身也经过一层代理,就可能塞入内网 IP(如 10.0.2.3)甚至恶意构造的 127.0.0.1

  • FILTER_FLAG_NO_PRIV_RANGE 能过滤掉 10.0.0.0/8172.16.0.0/12192.168.0.0/16127.0.0.0/8 等私有/环回地址
  • 不加这层校验,攻击者可能伪造 X-Forwarded-For: 127.0.0.1 绕过 IP 限流或权限校验
  • 注意:IPv6 的私有地址(如 fd00::/8)需额外处理,filter_var 不覆盖,生产环境建议用 ip_is_private() 类辅助函数

本地开发与测试时怎么模拟 LB 头

curlpostman 手动加请求头即可,例如:

curl -H "X-Real-IP: 203.0.113.45"       -H "X-Forwarded-For: 203.0.113.45, 192.168.0.100"       http://localhost/test-ip.php

在 Nginx 开发环境,可临时加配置验证头是否透传成功:

location / {     proxy_pass http://php-upstream;     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

关键点:别在没配 LB 的环境硬写逻辑去读 X-Forwarded-For,否则本地调试时会拿到空值或错误值;上线前务必确认 LB 实际发送了哪些头,用 print_r($_SERVER) 快速验证。

最常被忽略的是 LB 是否真的转发了头、以及是否启用了「追加模式」而非「覆盖模式」——这两点不确认清楚,IP 就永远拿不准。

text=ZqhQzanResources