首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 取得使用者真實 IP:穿透代理、負載平衡器與 CDN 的完整方案

     編輯:PHP 相關知識

IP 取得的核心挑戰

$_SERVER['REMOTE_ADDR'] 只能取得直接連線的客戶端 IP。若使用者經由代理、負載平衡器(Nginx/HAProxy)、CDN(Cloudflare/Akamai)或企業網路,該值將是代理伺服器 IP,非使用者真實 IP。需結合 X-Forwarded-For、X-Real-IP、CF-Connecting-IP 等標頭,並嚴格驗證信任代理清單。

標準標頭說明

標頭來源格式可信度
X-Forwarded-ForHTTP 代理/負載平衡器client, proxy1, proxy2(逗號分隔,最左為原始客戶端)需驗證信任代理
X-Real-IPNginx proxy_set_header單一 IP需確認 Nginx 設定
CF-Connecting-IPCloudflare單一 IP高(僅 Cloudflare 代理會送)
True-Client-IPAkamai/其他 CDN單一 IP需確認 CDN 文件
ForwardedRFC 7239 標準for=192.0.2.60;proto=https;by=203.0.113.43新標準,支援度逐漸提升

安全的 IP 取得函數(支援信任代理清單)

function getClientIp(array $trustedProxies = []): string {
    // 1. 預設直接連線 IP
    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

    // 2. 若無信任代理清單,僅回傳 REMOTE_ADDR(防止偽造標頭)
    if (empty($trustedProxies)) {
        return $ip;
    }

    // 3. 檢查 REMOTE_ADDR 是否在信任代理清單中
    $remote = $_SERVER['REMOTE_ADDR'] ?? '';
    $isTrusted = false;
    foreach ($trustedProxies as $proxy) {
        // 支援 CIDR 表示法(如 10.0.0.0/8, 192.168.1.1)
        if (filter_var($proxy, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            if ($remote === $proxy) { $isTrusted = true; break; }
        } elseif (strpos($proxy, '/') !== false) {
            if (ipInCidr($remote, $proxy)) { $isTrusted = true; break; }
        }
    }

    if (!$isTrusted) {
        return $ip; // 非信任代理,忽略所有轉發標頭
    }

    // 4. 依優先序檢查標頭
    $headers = [
        'HTTP_CF_CONNECTING_IP',      // Cloudflare
        'HTTP_TRUE_CLIENT_IP',        // Akamai
        'HTTP_X_REAL_IP',             // Nginx
        'HTTP_X_FORWARDED_FOR',       // 標準代理鏈
        'HTTP_FORWARDED',             // RFC 7239
    ];

    foreach ($headers as $header) {
        if (empty($_SERVER[$header])) continue;
        $val = $_SERVER[$header];

        // Forwarded 標頭需解析
        if ($header === 'HTTP_FORWARDED') {
            if (preg_match('/for=("?\[?([a-f0-9.:]+)\]?"?)/i', $val, $m)) {
                return $m[2];
            }
            continue;
        }

        // X-Forwarded-For 取第一個非私有 IP
        $ips = array_map('trim', explode(',', $val));
        foreach ($ips as $candidate) {
            if (filter_var($candidate, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                return $candidate;
            }
        }
        // 若全為私有 IP,回傳第一個
        if (!empty($ips)) return $ips[0];
    }

    return $ip;
}

// CIDR 比對輔助函數
function ipInCidr(string $ip, string $cidr): bool {
    list($subnet, $mask) = explode('/', $cidr);
    $mask = (int)$mask;
    $ipBin = ip2long($ip);
    $subnetBin = ip2long($subnet);
    if ($ipBin === false || $subnetBin === false) return false;
    $maskBin = -1 << (32 - $mask);
    return ($ipBin & $maskBin) === ($subnetBin & $maskBin);
}

實際部署設定範例

Nginx 反向代理

# nginx.conf
location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass https://backend;
}

PHP 信任代理設定(依架構調整)

$trustedProxies = [
    '10.0.0.0/8',        // 內網負載平衡器網段
    '192.168.1.10',      // 單一 Nginx 伺服器
    '172.16.0.0/12',     // Docker/容器網段
    // Cloudflare IPv4 範圍(官方文件更新時同步)
    '173.245.48.0/20',
    '103.21.244.0/22',
    // ... 更多範圍請參考 https://www.cloudflare.com/ips/
];

$realIp = getClientIp($trustedProxies);

常見應用場景與注意事項

  • 日誌記錄:記錄 $realIp 而非 REMOTE_ADDR,便於追蹤與分析。
  • 地理定位:搭配 MaxMind GeoIP2 資料庫,根據真實 IP 取得國家/城市。
  • 頻率限制/防刷:以真實 IP 為鍵值儲存 Redis 計數器,避免代理共用 IP 導致誤判。
  • 安全防護:WAF、防火牆規則需基於真實 IP,否則可能封鎖整個代理出口 IP。
  • 加密代理/VPN/Tor:無法穿透取得真實 IP,此為網路架構限制,非程式可解。
  • IPv6 支援:上述函數以 IPv4 為主,實際部署需擴充 FILTER_FLAG_IPV6、inet_pton 處理 IPv6 CIDR。

框架整合參考

  • Laravel:Request::setTrustedProxies([...], Request::HEADER_X_FORWARDED_ALL),再用 $request->ip()
  • Symfony:Request::setTrustedProxies([...], Request::HEADER_X_FORWARDED_AWS_ELB)
  • ThinkPHP:配置 app.request.trusted_proxies
  • CodeIgniter 4:$request->getIPAddress() 自動處理 Config\App::$proxyIPs
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved