Cloudflare Workers 调用 Gemini 时偶尔报 "User location is not supported":原因和几种解决办法
我在 Cloudflare Workers 上接入 Gemini API 时遇到一个问题:大多数请求都正常,但偶尔会直接收到 400 错误:
{
"error": {
"code": 400,
"message": "User location is not supported for the API use.",
"status": "FAILED_PRECONDITION"
}
}
报错是随机出现的,本地调试也复现不了,所以一开始很难判断是哪里出了问题。查过一圈资料后发现原因并不复杂,解决办法也有好几种。下面把整理结果记录下来。
一、根因:Gemini 判断的是 Worker 所在机房的位置
Cloudflare Workers 默认运行在离访问者最近的数据中心。Gemini API(generativelanguage.googleapis.com)判断地区时,看的是发起请求那台机器的出口 IP,也就是 Worker 当时所在的机房。API Key 归属哪里、最终用户在哪里,它都不管。
问题出在香港:中国大陆和香港都不在 Gemini API 的支持地区内。只要某次请求被分配到 HKG(香港)机房,Google 就会返回上面的地区错误。
“偶尔出现”是因为 Cloudflare 的路由会变,而且受影响的不只是中国大陆和香港用户:
- 一位在澳大利亚墨尔本的开发者发现,他访问的 Worker 跑在 4600 英里外的 HKG。这时直接调用和通过 AI Gateway 调用都会报同样的地区错误。来源
- 一位韩国开发者反映,同一个付费用户有时被分到 NRT(东京),一切正常;有时被分到 HKG,就直接报错。来源
- 菲律宾、越南的用户也有类似报告:本地直连 Gemini 正常,经过 Worker 就失败。来源 来源
一个最常见的误区要先说:改用 Cloudflare AI Gateway 不能解决这个问题。上面墨尔本那个案例里,AI Gateway 同样返回了地区错误。
怎么确认是不是这个原因
最省事的办法是看 Gemini 返回的响应头 cf-ray。它的末尾是机房代码,例如 88958fc801afb458-HKG,以 -HKG 结尾就基本可以确定。来源
也可以在 Worker 里把当前机房打到日志里:
console.log("colo:", request.cf?.colo);
二、解决方案
下面按我推荐的顺序排列,越靠前改动越小。
方案 1:Placement Hints,指定 Worker 的运行地区(首选)
Cloudflare 在 2026 年 1 月推出了显式放置提示(Placement Hints)。在 Wrangler 配置里声明一个云厂商区域,Worker 就会运行在离该区域延迟最低的 Cloudflare 机房,不再跟着访问者走。来源
# wrangler.toml
[placement]
region = "gcp:us-central1"
如果用 wrangler.jsonc:
{
"placement": {
"region": "gcp:us-central1"
}
}
region 的格式是 {provider}:{region},支持 AWS、GCP 和 Azure 的区域代码,例如 aws:us-east-1、gcp:europe-west1、azure:westeurope。来源 要调 Gemini,选一个美国的 GCP 区域比较合适。
已经有人用这个办法解决了同样的问题:菲律宾访客经常被分到 HKG,配置成靠近 gcp:us-central1 之后报错就消失了。来源
需要注意两点:
- 它不是 100% 的硬保证。 官方文档写明,请求量极高时(每秒数十万请求以上),Cloudflare 可能把实例分散到更大的范围来均衡流量。来源 对普通规模的业务来说基本够用。
- Terraform 用户要留意一个已知问题。
cloudflare_worker_version资源里的placement字段会被静默忽略,原因是 provider 把它发到了不支持该字段的 versions 接口。目前的绕行办法是部署后再调用一次脚本的 settings 接口,单独 PATCH placement。来源
顺带一提,旧的 Smart Placement(mode = "smart")是根据观测到的后端延迟自动放置的,它的目标是降低延迟,不负责避开受限地区,所以不建议拿它来解决这个问题。
方案 2:用 Durable Object 发出 Gemini 请求
如果需要更确定的出口位置,可以把“调用 Gemini”这一步单独交给一个 Durable Object,创建时通过 locationHint 指定地区(如 wnam 北美西部、enam 北美东部、weur 西欧)。Durable Object 创建后会停留在所在地区,外层 Worker 在哪个机房运行就无所谓了。
Durable Object 的定义:
import { DurableObject } from "cloudflare:workers";
export class GeminiProxy extends DurableObject {
async fetch(request) {
const url = new URL(request.url);
const target = new URL(
url.pathname + url.search,
"https://generativelanguage.googleapis.com"
);
return fetch(new Request(target, request));
}
}
外层 Worker 的转发:
export default {
async fetch(request, env) {
const id = env.GEMINI_PROXY.idFromName("us-proxy");
const stub = env.GEMINI_PROXY.get(id, { locationHint: "wnam" });
return stub.fetch(request);
},
};
Wrangler 配置:
[[durable_objects.bindings]]
name = "GEMINI_PROXY"
class_name = "GeminiProxy"
[[migrations]]
tag = "v1"
new_sqlite_classes = ["GeminiProxy"]
这个方案的代价有三点:
- 所有请求都要经过同一个(或少数几个)Durable Object 实例,并发高时要按 key 分片成多个实例。
- 要额外承担 Durable Object 的计费。
-
locationHint本身也是“提示”。它只在对象首次创建时起作用,Cloudflare 会尽量满足,但建议上线后用日志确认实际位置。
方案 3:识别地区错误后兜底重试
这个方案适合叠加在方案 1 或方案 2 之上:识别出地区错误后,换一个出口重发。
async function callGemini(geminiUrl, init, env) {
const res = await fetch(geminiUrl, init);
if (res.status === 400) {
const body = await res.clone().text();
if (body.includes("User location is not supported")) {
// 换到固定在北美的 Durable Object 出口重试
const id = env.GEMINI_PROXY.idFromName("us-proxy");
const stub = env.GEMINI_PROXY.get(id, { locationHint: "wnam" });
return stub.fetch(new Request(geminiUrl, init));
}
}
return res;
}
注意,如果 init.body 是流(ReadableStream),读过一次就不能再用。重试前要先把请求体读成字符串或 ArrayBuffer 再复用。
方案 4:出口交给地区可控的服务
还有一种做法是让 Cloudflare 只负责入口、鉴权和限流,真正请求 Gemini 的那一步放到出口地区可控的地方:
- 自建反向代理:在美国、日本、新加坡等受支持地区的 VPS 上跑一个转发服务。
- 能固定区域的 Serverless 平台:比如把 Vercel Functions 固定在
iad1,或在 GCP Cloud Run 的us-central1部署一个转发服务。 - 聚合平台:通过 OpenRouter 这类服务调用 Gemini,由它们的出口去请求 Google。
这是确定性最高的做法,代价是多一跳网络延迟,还多了一个需要维护的组件。
方案 5:改用 Vertex AI(需要自己验证)
通过 Vertex AI 调用 Gemini 时,走的是 GCP 项目鉴权,可以使用带区域的端点(例如 us-central1-aiplatform.googleapis.com),和面向开发者的 Gemini API 是两套体系。不过我没有查到 Vertex AI 怎样判断请求来源地区的公开资料,不能断定这样就能避开限制。如果考虑这条路,建议先从 HKG 出口实际测一下。
三、怎么选
| 场景 | 推荐做法 |
|---|---|
| 个人项目、中小流量,想用最小改动修好 | 方案 1(Placement Hints)+ 方案 3(兜底重试) |
| 核心付费功能,不能容忍偶发失败 | 方案 2(Durable Object + locationHint)或方案 4(固定出口) |
| 本来就在 GCP 上,或有企业合规要求 | 先实测方案 5(Vertex AI) |
我的建议是先在 wrangler.toml 里加上 [placement] region = "gcp:us-central1",再在代码里加一层地区错误检测和重试。这两步改动都很小,大部分偶发的 HKG 报错就能消失。上线后在日志里观察一段时间,如果还有漏网的请求,再考虑方案 2 或方案 4。
这个问题社区里早有讨论。2024 年就有人在 Cloudflare 社区提议,给 Worker 加一个“AI API Worker”标记,让平台自动避开不能访问主流大模型 API 的地区。来源 这个功能目前还没有落地。在它出现之前,Placement Hints 是现成方案里改动最小、也最接近这个思路的一个。
参考资料
- Cloudflare Docs:Placement
- Cloudflare Changelog:New Placement Hints for Workers(2026-01-22)
- Cloudflare Docs:Wrangler Configuration
- Cloudflare Community:Do not put Workers in LLM-forbidden locations
- Cloudflare Developers Discord 讨论:How to exclude specific data centers from Worker routing?
- GitHub:generative-ai-js Issue #151 Region Blocked Cloudflare Workers
- GitHub:Pin the chatbot Worker near a US Google Cloud region(PR 示例)
- GitHub:terraform-provider-cloudflare Issue #7206
- Google AI Developers Forum:Gemini API location error 400 when called from Cloudflare Workers