在 Web 開發中獲取用戶真實 IP 看似簡單,實則暗藏陷阱:反向代理、CDN、負載均衡器都會在請求鏈路中插入多層轉發標頭。單純讀取 $_SERVER['REMOTE_ADDR'] 往往只能拿到代理伺服器的 IP。以下函式按優先級依次檢查常見標頭,最大程度還原真實客戶端 IP。
<?php
/**
* 獲取客戶端真實 IP 地址
* 優先級:HTTP_CLIENT_IP > HTTP_X_FORWARDED_FOR > REMOTE_ADDR
*
* @return string IP 字串或「無法獲取!」
*/
function getClientIp(): string
{
$ipKeys = [
'HTTP_CLIENT_IP', // 標準代理標頭
'HTTP_X_FORWARDED_FOR', // 常見代理/CDN 標頭(可能包含多個 IP,逗號分隔)
'REMOTE_ADDR', // 直連或最後一級代理 IP
];
foreach ($ipKeys as $key) {
if (!empty($_SERVER[$key])) {
// X-Forwarded-For 可能包含 "client, proxy1, proxy2",取第一個
$ipList = explode(',', $_SERVER[$key]);
$clientIp = trim($ipList[0]);
// 簡單合法性過濾(可選:加入 filter_var 驗證)
if (filter_var($clientIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $clientIp;
}
// 若為私有/保留地址,繼續嘗試下一個標頭
}
}
return '無法獲取!';
}
// 使用範例
echo 'Client IP: ' . getClientIp();
?>
HTTP_CLIENT_IP 較少見但更直接;HTTP_X_FORWARDED_FOR 是事實標準,格式為 client, proxy1, proxy2,取首段即為原始客戶端。set_real_ip_from、Apache RemoteIPTrustedProxy)在 Web 伺服器層面重寫 REMOTE_ADDR,PHP 僅作為最後防線。filter_var 同時驗證 IPv4/IPv6,FILTER_FLAG_NO_PRIV_RANGE 排除私有網段(10.x、172.16-31.x、192.168.x),FILTER_FLAG_NO_RES_RANGE 排除保留網段。real_ip_header + set_real_ip_from 固化真實 IP 到 REMOTE_ADDR,PHP 直接讀取即可,無需複雜邏輯。<?php
function getRealIp(array $trustedProxies = []): string
{
// 1. 若已在 Web Server 層正確設定 real_ip,直接信任 REMOTE_ADDR
$remote = $_SERVER['REMOTE_ADDR'] ?? '';
if ($remote && filter_var($remote, FILTER_VALIDATE_IP)) {
// 若 REMOTE_ADDR 在信任清單內,說明已被上游正確重寫
if (empty($trustedProxies) || in_array($remote, $trustedProxies, true)) {
return $remote;
}
}
// 2. 回退:解析 X-Forwarded-For(需確保上游不可偽造)
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
// 從右向左找第一個不在信任清單的 IP
for ($i = count($ips) - 1; $i >= 0; $i--) {
if (!in_array($ips[$i], $trustedProxies, true) && filter_var($ips[$i], FILTER_VALIDATE_IP)) {
return $ips[$i];
}
}
}
return $remote ?: '未知';
}
// 範例:Nginx 信任 10.0.0.0/8 內部網段
$trusted = ['10.0.0.1', '10.0.0.2']; // 實際應用可用 CIDR 判斷庫
echo getRealIp($trusted);
?>
getClientIp() 即可滿足 90% 場景。HTTP_X_FORWARDED_FOR 原始值,便於事後追蹤。掌握 IP 獲取的層層邏輯,讓你的訪問日誌、風控系統、地理定位功能建立在可信的數據基礎之上。