request.getRemoteAddr() 僅能獲取直接連線的對端 IP。經過 Nginx、Apache、Squid、CDN、K8s Ingress 等反向代理後,真實客戶端 IP 隱藏在 HTTP 標頭中。本文提供生產級工具類,支援多層代理鏈、標準/非標準標頭、IPv6 回環轉換、內網 IP 過濾。
| 優先級 | 標頭名稱 | 來源/說明 | 格式範例 |
|---|---|---|---|
| 1 | X-Forwarded-For | 標準代理標頭,**最左側為最原始客戶端** | 203.0.113.195, 70.41.3.18, 150.172.238.178 |
| 2 | X-Real-IP | Nginx proxy_set_header X-Real-IP $remote_addr 常用 | 203.0.113.195 |
| 3 | Proxy-Client-IP | Apache HTTPD / WebLogic 等 | 203.0.113.195 |
| 4 | WL-Proxy-Client-IP | WebLogic 特有 | 203.0.113.195 |
| 5 | HTTP_CLIENT_IP | 部分代理/負載均衡器 | 203.0.113.195 |
| 6 | HTTP_X_FORWARDED_FOR | PHP $_SERVER 風格 | 同上 |
| 兜底 | request.getRemoteAddr() | 直接連線對端 (最後一層代理或客戶端) | 192.168.1.100 或 0:0:0:0:0:0:0:1 |
Nginx/Apache 代理配置關鍵
Nginx (nginx.conf)
RequestHeader set X-Forwarded-For "%{X-Forwarded-For}i, %{REMOTE_ADDR}e" RequestHeader set X-Real-IP "%{REMOTE_ADDR}e"進階議題
| 議題 | 處理策略 |
|---|---|
| 多層代理鏈 (CDN → Nginx → App) | X-Forwarded-For 會累積多個 IP,**取最左側非內網 IP**。需信任邊界:僅信任已知代理 IP 範圍內的標頭。 |
| 偽造標頭攻擊 | 攻擊者可偽造 X-Forwarded-For。解決:應用層僅信任來自受信任代理 IP (如 10.0.0.0/8) 的請求標頭;或使用 Spring Security XForwardedHeaderFilter 配置 trustedProxies。 |
| IPv6 地址 | 支援 2001:db8::1 格式。工具類已處理 IPv6 回環 ::1 轉 127.0.0.1。 |
| 內網穿透/VPN | 客戶端可能無公網 IP,RemoteAddr 為內網 IP。記錄時標註來源標頭名,便於審計。 |
Servlet 3.1+ HttpServletRequest.getRemoteAddr() | Tomcat 8.5+ 已內建 RemoteIpValve 自動解析 X-Forwarded-For 並重寫 getRemoteAddr()、getRemoteHost()。啟用方式:server.xml <Valve className="org.apache.catalina.valves.RemoteIpValve" />。啟用後工具類可簡化為直接回傳 request.getRemoteAddr()。 |