初學者指南:如何從請求中抓取對方的 IP 位址
更新日期: 2025 年 5 月 27 日
在開發 Web 應用程式或 API 時,有時你會需要知道用戶的 真實 IP 位址,例如:
- 做日誌紀錄(Logging)
- 限制某些地區存取
- 防止 DDoS 或濫用攻擊
- 建立地理位置分析
但在現代 Web 架構中(尤其是使用了代理伺服器、CDN 或反向代理時),抓 IP 並不是一件直觀的事。
本篇文章將一步步帶你了解如何正確從請求中抓出對方的 IP 位址。
什麼是用戶 IP?為什麼要特別抓?
什麼是「用戶 IP」?
IP(Internet Protocol)位址是網路中識別每個裝置的唯一代碼,就像住家的地址一樣,讓資料能正確傳送到對方。
在 Web 應用中,每當一位使用者打開網站、發送請求到伺服器,這個請求都會包含該使用者裝置的「來源 IP 位址」。
例如,一位使用者從筆電連上網站,他的請求中可能會帶有這樣的 IP:
203.0.113.45
這個 IP 就是你伺服器收到請求時,判斷對方是誰的依據之一。
為什麼我們要抓用戶的 IP?
抓取使用者 IP 可以幫助你:
- 做日誌紀錄(Logging):讓你知道是哪個 IP 發出的請求,便於錯誤追蹤與系統監控。
- 分析用戶行為與分布:例如統計不同地區的訪問量。
- 實作權限控制或封鎖名單:例如防止特定 IP 的攻擊或濫用。
- 防止作弊、重複操作:像是防止某人用機器重複填寫表單、投票等。
- 進行地理定位(Geo IP):根據 IP 判斷用戶可能來自哪個國家或城市,提供更符合在地需求的內容。
為什麼不是直接拿 req.ip
就好?
理論上,Web 伺服器會根據用戶的 TCP 連線,自動知道對方的 IP,你甚至可以透過像是 req.ip
(Express)、request.connection.remoteAddress
(Node.js 原生)直接拿到。
但在現實中,我們的伺服器通常 不是直接面對用戶,而是架設在以下這些結構中:
- Nginx、Apache 等反向代理伺服器
- CDN 服務(如 Cloudflare)
- 雲端負載平衡器(如 AWS Load Balancer)
- PaaS 平台(如 Heroku、Vercel)
這些架構會「幫你先接住使用者的請求」,然後再「轉送」給你的應用伺服器。這麼一轉,原本的來源 IP 就會被蓋掉,變成中介平台的 IP。
例如你抓到的可能是:
10.0.0.1
這是 AWS 的內部 IP,根本不是使用者的真實 IP。你就失去了上面所說的用途(風控、定位、追蹤等)。
解法:改從「請求標頭」抓 IP
為了讓你還是能知道「原本的用戶 IP 是誰」,這些中介層會在轉送請求時加上一些特殊的 HTTP Header(請求標頭),像是:
X-Forwarded-For
x-original-ip
x-real-ip
這些標頭會包含原始使用者的 IP。你的伺服器只要判斷這些標頭的值,就可以取得對方真正的 IP。
小結
概念 | 說明 |
---|---|
使用者 IP | 網路上辨識用戶裝置的代碼,會夾帶在請求中 |
直接取得 IP 的風險 | 代理/CDN 會蓋掉真實 IP,導致抓不到 |
解法 | 從標頭(Header)中抓出中介層補上的原始 IP |
只要你了解這些基礎概念,就能在不同的 Web 架構中,正確、安全地取得用戶 IP,不怕資料錯誤、日誌無效、風控失靈。
常見的 IP 標頭介紹
在使用代理伺服器(Proxy)、CDN 或負載平衡器的環境中,直接從伺服器的連線資訊取得的 IP(如 req.socket.remoteAddress
)通常是中介伺服器的 IP,而不是用戶端的真實 IP。
為了解決這個問題,這些中介系統會幫你在 HTTP 請求中加入一些標頭(Header),記錄原始用戶的 IP。
以下是幾個最常見、最實用的 IP 標頭介紹與使用建議。
X-Forwarded-For
:最常見、最重要
這是目前業界最常用的標頭,用於記錄整條代理鏈上的 IP 位址:
X-Forwarded-For: 203.0.113.45, 70.41.3.18, 150.172.238.178
- 第一個 IP(最左邊)是使用者最初的真實 IP。
- 後面的 IP 則是請求經過的每個代理伺服器所加入的,從左到右依序排列。
🧠 範例解析:
X-Forwarded-For: 客戶端IP, 第一層代理IP, 第二層代理IP
✅ 實務使用建議:
只要抓第一個 IP 通常就是用戶端的真實 IP:
const realIp = req.headers['x-forwarded-for']?.split(',')[0].trim();
⚠️ 注意:
- 如果代理鏈中的某個節點沒處理好或惡意竄改,也可能導致 IP 被偽造。
- 建議搭配可信任代理的設定(如 Express 中
app.set('trust proxy', true)
)使用。
x-original-ip
:次常見,平台依賴性高
這個標頭並不是業界標準,但在某些特定環境中會出現,例如:
- Microsoft Azure Application Gateway
- 自建代理伺服器(有些會自己加上這個欄位)
x-original-ip: 203.0.113.45
特點:
- 通常只會包含單一 IP
- 如果你無法取得
X-Forwarded-For
,這是個不錯的備援來源
✅ 建議:可以列為備用方案,例如:
const ip = req.headers['x-forwarded-for']?.split(',')[0].trim()
|| req.headers['x-original-ip']
|| req.socket.remoteAddress;
其他常見 IP 標頭
📌 X-Real-IP
- 常見於 Nginx 或其他自訂代理伺服器
- 通常只會傳送一個 IP(即使用者原始 IP)
X-Real-IP: 203.0.113.45
✅ 適用於你知道請求只經過單層代理時,例如你只用一層 Nginx 當作反向代理。
📌 CF-Connecting-IP
- Cloudflare 專屬標頭,只會有一個 IP
- 如果你用 Cloudflare,這是最可靠的來源
CF-Connecting-IP: 203.0.113.45
✅ 建議 Cloudflare 環境下優先採用,避免從 X-Forwarded-For 中誤抓到 Cloudflare 的節點 IP。
📌 True-Client-IP
- Akamai CDN 專屬標頭
- 原理與
CF-Connecting-IP
相同
True-Client-IP: 203.0.113.45
✅ 僅在你使用 Akamai 服務時才會出現,可優先參考。
📌 Forwarded
- RFC 7239 定義的標準化格式
- 支援記錄更多資訊,例如協定、代理等
Forwarded: for=203.0.113.45; proto=https; by=203.0.113.1
✅ 優點是格式嚴謹,可機器可讀
⚠️ 缺點是支援的系統還不普及,實務上還不常見。
補充:哪個 IP 標頭該優先讀取?
你可以設計一個順序邏輯來取得 IP:
function getClientIp(req) {
return req.headers['x-forwarded-for']?.split(',')[0].trim()
|| req.headers['cf-connecting-ip']
|| req.headers['true-client-ip']
|| req.headers['x-original-ip']
|| req.headers['x-real-ip']
|| req.socket.remoteAddress;
}
這樣設計的好處:
- 相容多種平台(Cloudflare、Nginx、Azure 等)
- 保留備援機制,不怕某個標頭缺失
小結
Header 名稱 | 適用情境 | 是否標準 |
---|---|---|
X-Forwarded-For | 通用、最常見 | ✅ 非正式業界慣例 |
x-original-ip | Azure、自建代理 | ❌ 非標準 |
X-Real-IP | Nginx | ❌ 非標準 |
CF-Connecting-IP | Cloudflare | ❌ 平台專屬 |
True-Client-IP | Akamai | ❌ 平台專屬 |
Forwarded | RFC 標準格式 | ✅ 標準 |
📌 提醒:HTTP 標頭容易被偽造,所以這些資訊僅適用於日誌、分析或非關鍵用途,不建議用來做嚴格的身份認證依據。
程式實作範例(Node.js)
以 Express 框架為例:
function getClientIp(req) {
const xForwardedFor = req.headers['x-forwarded-for'];
if (xForwardedFor) {
return xForwardedFor.split(',')[0].trim();
}
const xOriginalIp = req.headers['x-original-ip'];
if (xOriginalIp) {
return xOriginalIp;
}
const xRealIp = req.headers['x-real-ip'];
if (xRealIp) {
return xRealIp;
}
return req.socket.remoteAddress;
}
安全性注意事項
即使我們能從各種標頭中取得使用者 IP,但在實務上有許多安全風險需要特別注意,否則你可能會誤信錯誤的資訊、導致 安全漏洞 或 錯誤判斷。
以下是三個最關鍵的注意事項:
HTTP Header 可以被偽造
所有的 HTTP 標頭(Header)本質上都是 純文字資料,在發送請求的時候,任何客戶端都能任意添加或修改。
舉例來說,使用者可以在瀏覽器的開發者工具中、或透過 curl/Postman 發送這樣的請求:
GET /api/sensitive-data HTTP/1.1
Host: example.com
X-Forwarded-For: 1.2.3.4
這樣你伺服器讀到的 IP 就會是 1.2.3.4
,但事實上這根本不是用戶的真實 IP,而是他自己「聲稱」的 IP。
🚨 結論:
- 永遠不要把 IP 當作身份驗證依據
- 如果你在做登入白名單、敏感權限判斷時使用 IP,需要搭配其他驗證機制(如 Token、帳號系統)
需要搭配可信任的 Proxy 設定
如果你的應用程式部署在像是:
- Nginx → Node.js
- Heroku、Vercel、Cloudflare → Express
- AWS Load Balancer → EC2
這些都是「代理層轉發請求」的典型案例。
在這種情況下,你的應用程式需要明確告訴框架:「我信任這個代理,請從標頭中取回真實 IP」。
✅ Express 框架的做法:
app.set('trust proxy', true);
這一行的意思是:當我讀取 req.ip
時,Express 會自動幫你從 X-Forwarded-For
取第一個 IP,而不是使用 socket 的連線 IP。
⚠️ 如果你沒設定這一行,你可能會一直讀到錯誤的 IP,例如:
- Cloudflare 的 IP
- AWS 內網 IP(10.0.x.x)
使用 HTTPS 保護資料完整性
在 HTTP(非加密)傳輸中,請求內容很容易被竄改。舉例:
- 內部網路攻擊者可以攔截並修改
X-Forwarded-For
的內容 - 這會導致你的伺服器讀到錯誤的 IP,進而做出錯誤判斷(如放行不該放行的請求)
因此,在實際部署時,請務必使用 HTTPS,這樣可以:
- 確保請求內容在傳輸過程中不被第三方竄改
- 增加整體通訊的可信任程度
✅ 實務上,大多數現代雲平台(如 Heroku、Vercel、Cloudflare)都已預設使用 HTTPS,但若你自行部署伺服器(例如用 Nginx + Node.js),則需要自行配置 SSL 憑證。
快速總結:如何取得使用者 IP?
當你需要從一個請求中正確地判斷使用者的來源 IP,建議按照以下優先順序依序嘗試:
X-Forwarded-For
- 業界最通用的標頭,包含完整代理鏈,取第一個 IP 即為用戶 IP
x-original-ip
- 某些平台(如 Azure)專用,可做為備援
x-real-ip
- 常見於 Nginx 的簡化標頭
req.socket.remoteAddress
- 最後手段,僅適用於沒有經過代理伺服器的情況
範例實作(Node.js Express):
function getClientIp(req) {
return req.headers['x-forwarded-for']?.split(',')[0].trim()
|| req.headers['x-original-ip']
|| req.headers['x-real-ip']
|| req.socket.remoteAddress;
}
結語:IP 是有風險的參考資訊
抓取使用者 IP 是許多應用的基本需求,無論是日誌、分析、風控還是地理判斷。
但在現代架構中(尤其是有代理、CDN、平台中介的情況下),這件事遠比想像中複雜。
請記住:
- IP 不是安全機制,只是輔助資訊
- 對於 IP 的取得與解析,一定要考慮環境(有無代理?用的是哪一層?)
- 搭配框架正確設置、平台文件閱讀、HTTPS 使用,才是穩健解法
💡 小提示:若你用的是 Cloudflare、Heroku、Vercel 等平台,建議參考他們的官方文檔來決定使用哪一個標頭。