在 Web 開發中,獲取用戶的真實 IP 地址是常見需求,例如用於日誌記錄、訪問控制、地理定位或防刷機制。然而,由於代理服務器、負載均衡器、CDN 等中間層的存在,直接讀取 $_SERVER['REMOTE_ADDR'] 往往只能拿到代理的 IP,而非用戶真實 IP。本文整理了 5 種 PHP 獲取用戶 IP 的方法,並說明各自的適用場景與注意事項。
這是最簡單的方式,直接從 $_SERVER 超全局變量獲取:
<?php $ip = $_SERVER["REMOTE_ADDR"]; echo $ip; ?>
適用場景:無代理、無 CDN 的直連環境。
缺點:若用戶經過代理或 CDN,得到的將是代理服務器 IP,而非真實用戶 IP。
稍微進階一點,先檢查是否存在 HTTP_VIA(代理標識),若存在則嘗試從 HTTP_X_FORWARDED_FOR 取得原始 IP:
<?php $user_IP = ($_SERVER["HTTP_VIA"]) ? $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"]; $user_IP = ($user_IP) ? $user_IP : $_SERVER["REMOTE_ADDR"]; echo $user_IP; ?>
注意:HTTP_X_FORWARDED_FOR 可能包含多個 IP(格式如 client, proxy1, proxy2),此寫法未做解析,僅取整個字符串。
這是一個較完善的函數,會依序檢查 HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR,並過濾掉私有 IP 段(10.x、172.16.x、192.168.x),返回第一個公網 IP:
<?php
function getRealIp() {
$ip = false;
if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
if ($ip) {
array_unshift($ips, $ip);
$ip = false;
}
for ($i = 0; $i < count($ips); $i++) {
if (!preg_match("/^(10|172\.16|192\.168)\./", $ips[$i])) {
$ip = $ips[$i];
break;
}
}
}
return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}
echo getRealIp();
?>
優點:過濾內網 IP,提高獲取真實公網 IP 的準確度。
注意:原代碼使用已棄用的 eregi,已改用 preg_match。
早期 PHP 版本常用 $HTTP_SERVER_VARS 與 getenv(),此方法兼容性極佳:
<?php
if ($HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"]) {
$ip = $HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"];
} elseif ($HTTP_SERVER_VARS["HTTP_CLIENT_IP"]) {
$ip = $HTTP_SERVER_VARS["HTTP_CLIENT_IP"];
} elseif ($HTTP_SERVER_VARS["REMOTE_ADDR"]) {
$ip = $HTTP_SERVER_VARS["REMOTE_ADDR"];
} elseif (getenv("HTTP_X_FORWARDED_FOR")) {
$ip = getenv("HTTP_X_FORWARDED_FOR");
} elseif (getenv("HTTP_CLIENT_IP")) {
$ip = getenv("HTTP_CLIENT_IP");
} elseif (getenv("REMOTE_ADDR")) {
$ip = getenv("REMOTE_ADDR");
} else {
$ip = "Unknown";
}
echo $ip;
?>
適用場景:極老舊的 PHP 環境(PHP 4.x 時代),現代項目不建議使用。
簡潔版,僅依賴 getenv() 函數:
<?php
if (getenv('HTTP_CLIENT_IP')) {
$onlineip = getenv('HTTP_CLIENT_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR')) {
$onlineip = getenv('HTTP_X_FORWARDED_FOR');
} elseif (getenv('REMOTE_ADDR')) {
$onlineip = getenv('REMOTE_ADDR');
} else {
$onlineip = $HTTP_SERVER_VARS['REMOTE_ADDR'];
}
echo $onlineip;
?>
CF-Connecting-IP、X-Real-IP),並在 PHP 中優先讀取這些頭。獲取用戶真實 IP 看似簡單,實則需根據部署架構選擇合適策略。建議在項目初期統一封裝 getClientIp() 函數,並根據實際網絡環境持續優化,避免日後排查日誌時發現 IP 全是代理地址的尷尬局面。