「日誌裡怎麼每一筆操作紀錄的 IP 都長一樣?是不是全公司只有一個人在用系統?」
如果你的 Laravel 專案跑在 Docker、後面又有一層反向代理(Nginx、負載平衡器之類),這個問題的答案很可能不是「真的只有一個人在用」,而是應用程式從頭到尾都沒有拿到使用者的真實 IP,看到的永遠是反向代理自己的 IP。今天要講的就是這個坑,以及它怎麼被修好的。
TrustProxies middleware 在解決什麼問題HTTP 請求經過反向代理轉發時,應用程式收到的連線來源,技術上是反向代理的 IP,不是使用者瀏覽器發出請求的那個 IP。為了解決這個問題,反向代理通常會把使用者的真實 IP 塞進一個標頭(最常見的是 X-Forwarded-For),但應用程式預設不會自動信任這個標頭——原因很直接:如果隨便相信一個 HTTP 標頭裡宣稱的 IP,任何人都可以自己在請求裡加一個假的 X-Forwarded-For,偽裝成任何 IP。
所以 Laravel 提供了 TrustProxies 這個 middleware:明確告訴框架「只信任從這些特定 IP/網段送來的 X-Forwarded-For 標頭」,其他來源的偽造標頭一律忽略。
這個系列的素材專案裡,bootstrap/app.php 原本的設定是這樣:明確列出幾個信任的網段——通常是內網網段跟反向代理本身的固定 IP。這個設定在一般的雲端主機部署裡運作得很好:反向代理的 IP 是固定的,寫死在白名單裡剛好對得上。
但換到 Docker 環境之後,這個假設不再成立。Docker 容器之間的網路是動態配置的——每次容器重啟,內部的 IP 位址都可能不一樣,反向代理容器實際連進應用程式容器的來源 IP,跟白名單裡寫死的那個 IP 對不上。結果就是:TrustProxies middleware 判定「這個標頭不是從信任的來源送來的」,直接忽略掉 X-Forwarded-For,應用程式看到的永遠是 Docker 內部網路裡那個變動的來源 IP——難怪日誌裡的操作 IP 看起來都長得差不多,甚至完全一樣。
修法是把白名單改成信任「所有」代理來源。用一組對照來看這個差異:
❌ 白名單信任特定網段(在傳統主機部署下沒問題)
$middleware->trustProxies(at: ['192.168.0.0/16', '固定IP/24']);
→ Docker 環境下容器間 IP 動態配置,
反向代理的實際來源 IP 常常對不上白名單,
導致 X-Forwarded-For 被忽略,永遠拿不到真實 IP
✅ 信任所有代理來源
$middleware->trustProxies(at: '*');
→ 不管反向代理的來源 IP 是什麼,都信任它宣稱的
X-Forwarded-For,正確還原出使用者真實 IP
看到這裡,可能會有一個疑問:一開始不就是因為「不能隨便信任標頭裡宣稱的 IP」才需要白名單嗎?現在直接改成「信任所有」,不就繞回原本要防的問題了嗎?
答案是:這個寫法能安全成立,前提是應用程式本身不會被外部直接連到——也就是說,前面一定要有一層真正可信的反向代理或負載平衡器擋著,外部流量沒辦法繞過這層代理、直接對應用程式容器發出偽造的 X-Forwarded-For。在容器化部署裡,這通常靠網路層的隔離來保證(應用程式容器不對外開放連接埠,只有反向代理能連到它)。
如果你的應用程式本身就是直接暴露在公開網路上、沒有反向代理擋在前面,那麼「信任所有代理」這個設定會讓任何人都能透過偽造標頭來假冒任意 IP——這時候該做的不是複製這個修法,而是先確認自己的部署架構有沒有那層可信的邊界。
如果你的專案也跑在容器化環境、後面有反向代理,回想一下:你上一次檢查系統記錄的操作 IP,是不是真的驗證過那是使用者的真實 IP,還是其實一直都是同一個內部網路位址,只是沒有人發現?
X-Forwarded-For 標頭還原TrustProxies middleware 決定要不要信任這個標頭,避免被偽造的標頭欺騙明天要看兩支輕量但用途完全不同的 middleware——一支只是加一個安全性標頭,另一支則是把「依網域判斷該不該重導」這種業務邏輯放進了路由層。middleware 能做的事,比很多人以為的更廣。