如果你把 Google / Gemini 這類流量單獨分到 Cloudflare WARP,遲早會碰到一種很迷惑的故障:WARP 明明是 Connected,網頁甚至還能正常開啟,但某個 Gemini API 或客戶端突然開始返回:

HTTP 400
FAILED_PRECONDITION
User location is not supported for the API use.

我這次遇到的就是這種情況。更容易誤導人的地方在於,Gemini 網頁端仍然正常,出問題的是另一個走 Gemini 後端的客戶端。第一反應很容易變成“是不是帳號被限制了”“是不是 Xray 路由壞了”“是不是要重配 Nginx”,但最後真正有效的動作反而很小:先確認 Gemini 實際看到的 WARP 出口,然後重連 WARP,再比較前後的出口變化。

這套排查方法適合一種很具體的架構:伺服器上的 WARP 以本機 SOCKS5 代理執行,Xray 只把指定的 Google / Gemini TCP 流量送進這個 SOCKS5。入口可能還是 Cloudflare + Nginx + VLESS/WS,但入口和出口是兩件完全不同的事。

真正要看的不是 VPS IP,而是最後一跳 WARP 出口

我現在更習慣先把鏈路畫出來:

客户端
  ↓
Cloudflare / Nginx / Xray
  ↓
Google / Gemini 流量
  ↓
127.0.0.1:40000
  ↓
Cloudflare WARP
  ↓
Gemini

這裡最容易判斷錯的是 VPS 的 Origin IP。它只是伺服器自己的公網地址;只要 Gemini 流量已經被 Xray 送進 WARP,Google 最終看到的就應該是 WARP 出口,而不是這臺 VPS 的原生出口。

Cloudflare 對 WARP Local proxy 的定義也很明確:只有顯式設定到 HTTPS 或 SOCKS5 本地代理的應用流量會經過 WARP,其餘流量仍按普通網路連線出去。所以排查時,最好直接對這個 SOCKS5 做測試,而不是看瀏覽器、VPS 公網地址,或者只看 warp-cli status。

我會先記下當前出口:

curl -s --socks5-hostname 127.0.0.1:40000 \
  https://www.cloudflare.com/cdn-cgi/trace \
  | grep -E '^(ip|loc|warp)='

正常情況下,你會得到類似:

ip=<warp-egress-ip>
loc=US
warp=on

這裡三個欄位的意義不一樣:

  • warp=on 只能證明這次請求確實經過了 WARP。
  • ip= 是目標網站實際看到的出口地址,也是這次排查最值得記錄的值。
  • loc= 是一個地理線索,但不是“這個客戶端一定會接受”的保證。

最後一點很重要。2026 年 Google AI Developers Forum 上也有開發者報告:伺服器明明位於 Gemini 支援地區,外部地理庫看起來也正常,Gemini API 仍可能返回同樣的 User location is not supported。所以 loc=US、loc=CA 這種結果更適合拿來判斷方向,而不是直接當成最終結論。

我最後用的還原動作很簡單:記錄、重連、再比較

如果 WARP 本身健康,而錯誤又明顯指向 location / eligibility,我不會先動 Xray、Nginx 或 Cloudflare 入口。先做一次最小變化的 WARP 重連:

echo "=== BEFORE ==="
curl -s --socks5-hostname 127.0.0.1:40000 \
  https://www.cloudflare.com/cdn-cgi/trace \
  | grep -E '^(ip|loc|warp)='

warp-cli --accept-tos disconnect
sleep 5
warp-cli --accept-tos connect
sleep 10

echo "=== AFTER ==="
curl -s --socks5-hostname 127.0.0.1:40000 \
  https://www.cloudflare.com/cdn-cgi/trace \
  | grep -E '^(ip|loc|warp)='

這次實際故障裡,WARP 重連前後拿到了不同的共享出口,warp=on 仍然成立,位置欄位也保持在支援區域;重新測試客戶端以後,原來的地區錯誤消失了。

這個結果能說明的是:舊 WARP 出口是觸發因素之一。它並不能證明“某個 WARP IP 永遠有問題”,也不能反推出 Gemini 的內部風控或地理分類規則。對排障來說,能把問題從“整條代理鏈”收窄到“最後一跳出口”已經很有價值。

如果重連以後 IP 沒變,我一般再嘗試一次,但不會把下面這組命令當成常規換 IP 手段:

warp-cli registration delete
warp-cli registration new

註冊身份和共享公網出口不是一一繫結關係。刪除註冊再建立,既不能保證換到新出口,還會把一個原本健康的 WARP 註冊狀態重新引入變數。對線上節點來說,這個代價通常不值得。

我更願意把順序固定成:

确认 WARP SOCKS5 可用
        ↓
记录 ip / loc / warp
        ↓
disconnect / connect
        ↓
再次记录出口
        ↓
出口变化 → 重测 Gemini / 客户端
        ↓
仍失败 → 再查路由、IPv4/IPv6、DNS 或服务端分类

這樣做的好處是,每一步只改變一個變數。尤其是當你的入口已經穩定執行時,不要因為一個 FAILED_PRECONDITION 就順手改 Nginx、VLESS、Cloudflare DNS 和 Xray 路由。改得越多,越難知道到底是哪一步還原了。

還有一個邊界需要說清楚:普通 WARP 並不是可控的住宅代理池。Cloudflare 官方明確寫著,WARP 不用於偽裝成來自另一個國家;普通共享出口也不能讓你指定“美國某城市”或某個固定公網 IP。你可以透過斷開 / 重連觀察共享出口是否變化,但不能把這種變化當成確定性的輪換能力。

如果你的業務真正需要固定、獨享、可加入白名單的出口,Cloudflare 提供的是 Zero Trust Enterprise 的 Dedicated Egress IP。它是賬戶獨享的靜態出口,並繫結具體 Cloudflare 資料中心;這和普通 WARP 共享出口不是一個產品層級。對個人開發者來說,大多數時候沒必要因為偶發的 Gemini 分類問題直接升級到這一步,但它至少說明了“穩定固定出口”應該在哪一層解決。

我現在把這類故障分成兩種來想:如果連 WARP SOCKS5 自己都不通,那是 WARP 服務或本機代理問題;如果 SOCKS5 正常、warp=on,但 Gemini 仍然返回 location 錯誤,就把注意力放到出口地址、協議路徑和上游分類,不要先重做整條入口。

如果你也在把多個 AI 工具收進自己的代理層,可以繼續看 Codex 怎麼透過 CLIProxyAPI 反代:Docker 部署、OAuth 登入和客戶端設定。那篇更偏統一 AI 接入層,這篇則更像是“出口已經分流之後,出了問題怎麼快速把範圍縮小”。