CDN/反向代理轉發請求時,原始客戶端 IP 通過以下 Header 傳遞 (優先級遞減):
HTTP_CLIENT_IP (部分代理)HTTP_X_FORWARDED_FOR (標準,格式:client, proxy1, proxy2)REMOTE_ADDR (直接連接或最後一級代理 IP)關鍵:X-Forwarded-For 可偽造,**必須**從左向右取第一個**非內網/保留地址**的 IP。
<?php
function getRealIp(): string {
$ip = false;
// 1. HTTP_CLIENT_IP
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
}
// 2. HTTP_X_FORWARDED_FOR (可能包含多個 IP)
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
if ($ip) array_unshift($ips, $ip); // 合併優先級
foreach ($ips as $candidate) {
// 過濾私有/保留地址段
if (!preg_match('/^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/', $candidate)) {
$ip = $candidate;
break;
}
}
}
// 3. 回退 REMOTE_ADDR
return $ip ?: ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
}
// 使用
$realIp = getRealIp();
?>
文件:include/main.inc.php
// 替換原有獲取 IP 邏輯為:
$_SGLOBAL['onlineip'] = getRealIp();
文件:source/class/discuz/discuz_application.php 或 config/config_global.php (新版)
// 修改 $_G['clientip'] 獲取邏輯
$_G['clientip'] = getRealIp();
或在 config/config_global.php 直接定義:
$_config['security']['ipcheck'] = 1; // 開啟 IP 檢查
// 配合插件或修改核心函數 getonlineip()
文件:e/class/connect.php 中的 egetip() 函數
function egetip() {
return getRealIp(); // 替換原有實現
}
現象:後台開啟「註冊 IP 與邀請碼擁有者 IP 不一致則禁止」導致誤攔截。
方案 A:註釋攔截邏輯 (source/module/member/member_register.php)
// showmessage('register_invite_iperror');
方案 B:修正 IP 獲取 (同上)
<%
Function CheckIP(ipStr)
Dim regEx
Set regEx = New RegExp
regEx.Pattern = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
regEx.Global = False
regEx.IgnoreCase = False
CheckIP = regEx.Test(ipStr)
Set regEx = Nothing
End Function
Function GetClientIP()
Dim clientIP, arr, i
If CheckIP(Request.ServerVariables("HTTP_CLIENT_IP")) Then
GetClientIP = Request.ServerVariables("HTTP_CLIENT_IP")
Exit Function
End If
arr = Split(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",")
For i = 0 To UBound(arr)
clientIP = Trim(arr(i))
If CheckIP(clientIP) Then
GetClientIP = clientIP
Exit Function
End If
Next
GetClientIP = Request.ServerVariables("REMOTE_ADDR")
End Function
%>
// 1. 標準屬性 (受代理影響)
string ip1 = HttpContext.Current.Request.UserHostAddress;
// 2. ServerVariables
string ip2 = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
// 3. DNS 解析 (獲取服務器自身 IP,非客戶端)
string hostName = System.Net.Dns.GetHostName();
string ip3 = System.Net.Dns.GetHostAddresses(hostName)[0].ToString();
// 4. 無視代理獲取真實 IP (X-Forwarded-For)
string ip4 = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
// 5. 綜合判斷 (推薦)
public string GetRealClientIP() {
var request = HttpContext.Current.Request;
if (request.ServerVariables["HTTP_VIA"] != null) { // 經過代理
string xff = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(xff)) {
// 取第一個非內網 IP
foreach (var ip in xff.Split(',')) {
ip = ip.Trim();
if (IsPublicIP(ip)) return ip;
}
}
}
return request.ServerVariables["REMOTE_ADDR"];
}
private bool IsPublicIP(string ip) {
// 簡單私有段過濾
return !Regex.IsMatch(ip, @"^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)");
}
<script>
var ip = '<!--#echo var="REMOTE_ADDR"-->'; // 需 SSI 支持
alert("Your IP: " + ip);
// ActiveX (僅 IE、需用戶允許、極不推薦)
function GetLocalIPAddress() {
try {
var obj = new ActiveXObject("rcbdyctl.Setting");
return obj.GetIPAddress;
} catch(e) { return ""; }
}
</script>
string ip;
if (Context.Request.ServerVariables["HTTP_VIA"] != null) {
ip = Context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
} else {
ip = Context.Request.ServerVariables["REMOTE_ADDR"].ToString();
}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 覆蓋客戶端值)。proxy_add_x_forwarded_for 追加而非覆蓋。VARBINARY(16) 或 VARCHAR(45)。$_SERVER 鍵為大寫 (HTTP_X_FORWARDED_FOR),Nginx 轉發時設置 proxy_set_header X-Forwarded-For ... (大小寫不敏感)。