本文目录导读:

在 PHP 项目中处理多层 CDN(如 Cloudflare → 阿里云 CDN → 源站)时,需要通过逐层判断 HTTP 头部来获取真实访客 IP,以下是标准实现方案:
核心原理
CDN 通常在请求头中传递原始 IP:
X-Forwarded-For:最常用,可包含逗号分隔的多个IPX-Real-IP:部分CDN使用(如Nginx)CDN-Specific-IP:特定CDN的私有头(如 Cloudflare 的CF-Connecting-IP)
多层CDN解析策略
function getRealClientIP() {
// 1. 检查专用CDN头(优先)
$cdnHeaders = [
'HTTP_CF_CONNECTING_IP', // Cloudflare
'HTTP_X_FORWARDED_FOR', // 标准代理
'HTTP_X_REAL_IP', // Nginx代理
'HTTP_X_AKAMAI_EDGESERVERIP', // Akamai
'HTTP_TRUE_CLIENT_IP', // Cloudflare Enterprise
'HTTP_FASTLY_CLIENT_IP', // Fastly
'HTTP_INCAP_CLIENT_IP', // Incapsula
'HTTP_CLIENT_IP' // 通用
];
foreach ($cdnHeaders as $header) {
if (isset($_SERVER[$header]) && !empty($_SERVER[$header])) {
$ip = $_SERVER[$header];
// 2. 处理 X-Forwarded-For 多IP情况(从右到左排除可信代理)
if ($header === 'HTTP_X_FORWARDED_FOR') {
$ip = parseXForwardedFor($ip);
}
// 3. 验证IP有效性
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
// 4. 最终fallback到REMOTE_ADDR
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
* 解析X-Forwarded-For多级IP
* 示例:X-Forwarded-For: client, proxy1, proxy2
* 策略:取最左侧有效公网IP(排除CDN节点IP)
*/
function parseXForwardedFor($headerValue) {
$ips = explode(',', $headerValue);
$ips = array_map('trim', $ips);
// 定义可信CDN节点IP段(根据实际CDN提供商配置)
$trustedProxies = [
'173.245.48.0/20', // Cloudflare
'103.21.244.0/22', // Cloudflare
'103.22.200.0/22', // Cloudflare
'103.31.4.0/22', // Cloudflare
'141.101.64.0/18', // Cloudflare
'108.162.192.0/18', // Cloudflare
'190.93.240.0/20', // Cloudflare
'188.114.96.0/20', // Cloudflare
'197.234.240.0/22', // Cloudflare
'198.41.128.0/17', // Cloudflare
'162.158.0.0/15', // Cloudflare
'104.16.0.0/12', // Cloudflare
'172.64.0.0/13', // Cloudflare
'131.0.72.0/22', // Cloudflare
// 阿里云CDN
'106.11.0.0/16',
'218.30.0.0/15',
// ... 其他CDN厂商IP段
];
// 从右向左扫描,跳过可信代理IP
for ($i = count($ips) - 1; $i >= 0; $i--) {
$ip = $ips[$i];
if (!isTrustedProxy($ip, $trustedProxies)) {
return $ip;
}
}
// 如果所有IP都是可信代理,返回最左侧
return $ips[0] ?? $_SERVER['REMOTE_ADDR'];
}
function isTrustedProxy($ip, $trustedProxies) {
foreach ($trustedProxies as $cidr) {
if (ipInCIDR($ip, $cidr)) {
return true;
}
}
return false;
}
/**
* CIDR匹配函数
*/
function ipInCIDR($ip, $cidr) {
list($subnet, $mask) = explode('/', $cidr);
if ((ip2long($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet)) {
return true;
}
return false;
}
集成方案(最佳实践)
配置文件(config/cdn.php)
return [
'trusted_proxies' => [
// Cloudflare IP ranges
'173.245.48.0/20',
'103.21.244.0/22',
// ... 完整列表从CDN官网获取
// 阿里云CDN
'106.11.0.0/16',
// 自定义内网CDN
'10.0.0.0/8',
'172.16.0.0/12',
],
'ip_headers' => [
'CF-Connecting-IP',
'X-Real-IP',
'X-Forwarded-For',
'True-Client-IP',
],
];
中间件实现(Laravel示例)
namespace App\Http\Middleware;
use Closure;
class ResolveClientIP
{
public function handle($request, Closure $next)
{
$clientIP = $this->getClientIP($request);
$request->merge(['client_ip' => $clientIP]);
return $next($request);
}
private function getClientIP($request)
{
// Cloudflare优先
if ($request->hasHeader('CF-Connecting-IP')) {
return $request->header('CF-Connecting-IP');
}
// X-Forwarded-For
if ($request->hasHeader('X-Forwarded-For')) {
$ips = explode(',', $request->header('X-Forwarded-For'));
// 从右到左排除可信代理
foreach (array_reverse($ips) as $ip) {
$ip = trim($ip);
if (!$this->isTrustedProxy($ip)) {
return $ip;
}
}
}
return $request->ip();
}
private function isTrustedProxy($ip)
{
$proxies = config('cdn.trusted_proxies');
foreach ($proxies as $cidr) {
if ($this->ipInCIDR($ip, $cidr)) {
return true;
}
}
return false;
}
}
测试验证方法
测试脚本
// test.php
$_SERVER = [
'REMOTE_ADDR' => '1.2.3.4',
'HTTP_X_FORWARDED_FOR' => '203.0.113.5, 198.51.100.1, 192.0.2.1',
'HTTP_CF_CONNECTING_IP' => '203.0.113.5',
];
echo "Client IP: " . getRealClientIP();
// 输出: 203.0.113.5 (因为X-Forwarded-For最左侧为203.0.113.5)
使用curl模拟测试
# 模拟三层CDN
curl -H "X-Forwarded-For: 真正的公网IP, CDN1节点IP, CDN2节点IP" \
-H "CF-Connecting-IP: 真正的公网IP" \
https://yourdomain.com/api/check-ip.php
安全注意事项
- 不要信任REMOTE_ADDR:在多CDN环境下,它通常只代表最后一层CDN节点
- 定期更新CDN IP段:从各CDN厂商获取最新IP范围
- 考虑IP伪造风险:如果HTTP头可被伪造,需要验证请求是否真的经过CDN
- 使用HTTPS:防止中间人篡改头部信息
- 记录请求链路:建议记录完整IP链用于调试
生产环境配置示例
# Nginx 配置示例
server {
set_real_ip_from 10.0.0.0/8; # 内网CDN
set_real_ip_from 173.245.48.0/20; # Cloudflare
real_ip_header X-Forwarded-For;
real_ip_recursive on; # 关键:开启递归查找
}
通过这种分层策略,可以准确获取经过多层CDN代理后的真实访客IP,同时避免伪造IP攻击,建议在框架层面统一实现并缓存结果,避免每次请求重复计算。