Cloudflare Workers Custom Domain 与 Route 使用指南
Custom Domain 和 Route 不是两种绑域名写法,而是两条请求链路:Worker 作为 hostname origin,或在既有源站前按 URL 拦截。
约三千九百字·读约十二分钟 · English
Cloudflare Workers 控制台把 Add Custom Domain 和 Add Route 放在同一个位置,初看很像两种“给 Worker 绑域名”的写法。部署时才会发现,它们定义的是两条不同的请求链路。
Custom Domain 把一个完整 hostname 的源站交给 Worker;Route 则让 Worker 按 URL 规则先接住请求,再决定是否交给已有源站。 api.example.com 本身就是 Worker 应用时,用 Custom Domain。站点或 API 已经有源站,只想在 /admin/*、/checkout/* 前插入逻辑时,用 Route。Cloudflare 也以“Worker 是否是应用 origin”作为这两个功能的主要分界线。
两条链路,两个职责
给 api.example.com 配置 Custom Domain 后,Cloudflare 会为该 hostname 创建 DNS record,并签发所需证书。/users、/login 和 /v1/orders?status=open 都会交给同一个 Worker;路径和 query string 不参与 Custom Domain 的绑定判断。此时 Worker 就是该 hostname 的 origin,后续是否访问 D1、R2、KV 或第三方 API,完全由代码决定。
Route 则建立在已有 hostname 上。请求先按 URL pattern 匹配 Route;命中后 Worker 执行。若代码调用 fetch(request),Cloudflare 会依据当前 zone 的 DNS 配置把请求继续发往原有应用源站。因此,Route 很适合承载鉴权、重写、缓存、限流、审计、灰度和反向代理等前置逻辑。

图 1:Custom Domain 将 Worker 作为 origin;Route 则让 Worker 位于既有源站之前。
| 对比项 | Custom Domain | Route |
|---|---|---|
| 要解决的问题 | 将一个 hostname 的 origin 指向 Worker | 将一类 URL 请求映射到 Worker |
| 常见用途 | 新 API、Webhook、BFF、无传统服务器的边缘应用 | 既有站点前的鉴权、缓存、重写、分流、反向代理,以及复用已有 CNAME 做入口优选 |
| 触发范围 | 精确 hostname 下的全部路径 | 可按 scheme、host、path 匹配 |
| DNS 前提 | Cloudflare 创建所需 DNS record | hostname 必须已有经 Cloudflare 代理的 DNS record |
fetch(request) 的常见语义 | Worker 主动请求所需依赖 | 按 zone DNS 继续请求既有 origin |
| 同 zone Worker 访问 | 可通过 hostname 用 fetch() 调用 | 不能作为同 zone fetch() 的目标 |
这不是“功能多一点或少一点”的问题,而是应用边界不同。
各自的长处和限制
Custom Domain 的好处是语义直接:域名就是 Worker 应用的入口。Cloudflare 管理 DNS record 和证书;当 Worker 本身就是 API 或应用后端时,不必额外维护一个只为回源而存在的占位服务器。它还可以成为同一 zone 内其他 Worker 的 HTTP fetch() 目标。
它的约束也很明确。绑定以精确 hostname 为单位,不能只接管其中一个 path,也不支持 wildcard DNS。根域和 www 需要分别处理;已有 CNAME 的 hostname 不能直接创建 Custom Domain;删除后还应检查遗留的 Advanced Certificate。
Route 的长处是细。你可以只处理一个 path 前缀、某个子域或某种 scheme,且多个规则同时命中时由最具体的 pattern 胜出。这样可以不动原有源站,先把鉴权、缓存、日志或灰度放到一小部分流量上,再逐步扩大范围。
代价是需要更仔细地配置和排障。Route 依赖已有的代理 DNS record;pattern 有通配符、大小写和 query string 的边界;代码也要清楚地区分“直接返回”和“fetch(request) 回源”。另外,Route Worker 不能作为同一 zone 的 HTTP fetch() 目标,内部调用应使用 Service Binding。
| 维度 | Custom Domain | Route |
|---|---|---|
| 应用入口 | Worker 直接成为 hostname origin | 可在原站前逐步接入 Worker,但不自动取代 origin |
| DNS 与 TLS | 自动创建 record 并签发所需证书;CNAME 冲突时需先迁移 | 复用既有 DNS 和源站;record 必须被 Cloudflare 代理 |
| 路由能力 | 该 hostname 的所有 path 都进入 Worker;没有 path 级绑定 | 可按 scheme、host、path 精确匹配;不是正则,不能匹配 query |
| 服务组合 | 可按 hostname 被同 zone Worker 调用;对纯内部服务会增加公开入口 | 适合放在业务 Worker 前做策略层;不能作为 same-zone fetch() 目标 |
| 变更风险 | 适合新服务或纯 Worker origin;迁移时要确认回源依赖 | 适合低风险的按路径改造;Worker 必须明确处理回源和失败分支 |
用一个问题做选择:Worker 是不是最终源站?
如果 api.example.com 没有传统服务器,Worker 自己负责 API 路由、数据访问和外部调用,那么它就是这个 hostname 的最终源站。Custom Domain 的 DNS、证书和流量模型与这个事实一致。
如果 www.example.com 仍由 Next.js、Nginx、Kubernetes 或 SaaS 源站提供页面,而你只想让 /admin/* 先检查 Token,或者让 /checkout/* 先过一道风控,则不必将整个 hostname 交给 Worker。Route 可以截获这部分流量,再根据结果直接响应或 fetch(request) 回源。
Custom Domain 管“域名由谁服务”;Route 管“哪些请求先交给 Worker”。
场景:用 Route 接优选 CNAME,改善国内访问
站点已经部署在 Cloudflare workers 上时,国内用户有时会碰到默认 Anycast 入口路由差、延迟高的问题。社区里常见的做法不是换掉 Worker,而是换一条更好的进入 Cloudflare 的 IP,再把原来的 hostname 绑到 Worker 上。
这条链路通常是:把 www.example.com CNAME 到一条优选 CNAME,例如 visa.cn;然后在对应 Worker 上添加 Route,pattern 写成 www.example.com/*。用户访问 www.example.com 时,解析先走到优选节点给出的 Cloudflare IP,请求进入边缘后 Host 仍是 www.example.com,Route 命中,流量就绑定到这个 Worker。Worker 再按业务 fetch() 真正的站点,例如 Pages、另一套源站,或已有应用。
这里必须用 Route,不能用 Custom Domain。Custom Domain 会由 Cloudflare 创建并接管该 hostname 的 DNS record,无法继续把 www.example.com 指到 visa.cn 这类优选 CNAME;hostname 上已有 CNAME 时,Custom Domain 本身也创建不了。Route 不改写你已经选好的解析,只按 URL 把进来的请求交给 Worker。
[[routes]]
pattern = "www.example.com/*"
zone_name = "example.com"
优选解决的是“从哪一个 Cloudflare IP 进网”,不是绕过 Cloudflare。还要注意权威解析在哪里:如果这条记录由 Cloudflare 权威 DNS 托管,并且处于已代理(橙色云)状态,用户拿到的会是 Cloudflare 自己的代理 IP,CNAME 目标不会成为实际连上的地址。因此优选 CNAME 通常放在你能控制最终解析结果的 DNS 上,同时保证 example.com 这个 zone 以及 www.example.com 仍被 Cloudflare 识别,Route 和证书才能按 Host 生效。
这不是 Cloudflare 官方产品能力。优选节点会变化,需要自己维护;命中 Route 的请求也会计入 Workers 用量。
Wrangler 里都叫 routes,含义却不一样
两者都可以写在 Wrangler 的 routes 配置中,所以容易被误当成同一类规则。关键是 custom_domain = true:加上它,配置表达的是“这个 Worker 是该 hostname 的 origin”,不再只是“匹配一条 URL 规则”。
# Custom Domain:Worker 是 api.example.com 的 origin
[[routes]]
pattern = "api.example.com"
custom_domain = true
# Route:只在既有站点上拦截 /api/ 请求
[[routes]]
pattern = "www.example.com/api/*"
zone_name = "example.com"
第二段配置要求 www.example.com 已可解析,并且该 DNS record 已被 Cloudflare 代理。Worker 既可以自行返回响应,也可以用 fetch(request) 将请求交还给对应的应用源站。
Route 能按路径挑请求,Custom Domain 不能
Custom Domain 只按 hostname 精确匹配。绑定 api.example.com 不会自动覆盖 www.example.com,也不支持 wildcard DNS。因此,根域和 www 要分别配置,或用 Redirect Rule 将其中一个版本跳转到另一个版本。
Route 的规则更细,但它不是正则表达式。pattern 只支持 *,可以指定 scheme、host 和 path,并在多条规则同时命中时优先选择最具体的一条。它不能包含 query parameter,也不能在路径中间使用通配符,例如 example.com/*.jpg 不合法;path 还区分大小写。
另一个容易踩到的边界是 *example.com/*。它可能匹配 myexample.com,而不只是 example.com 及其子域。若目标确实是根域和子域,应分别写成 example.com/* 与 *.example.com/*。
| 需求 | 更合适的方式 | 原因 |
|---|---|---|
api.example.com 的全部路径由 Worker 提供 API | Custom Domain | Worker 是 hostname 级 origin。 |
只有 /admin/* 需要经过鉴权 Worker | Route | 需要 path 级匹配。 |
在现有 www.example.com 前记录审计日志 | Route | Worker 在 origin 之前执行。 |
把 www.example.com CNAME 到优选域名,再让 Worker 承接国内访问 | Route | 需要保留已有 CNAME,只按 hostname 绑定 Worker。 |
根域和 www 都要响应 | 两个明确配置,或重定向 | 一个 Custom Domain 不会自动覆盖另一个 hostname。 |
| 按 query string 分给不同 Worker | 在 Worker 代码中判断,或使用 binding | Route pattern 不支持 query parameter。 |
可以组合:Route 在前,Custom Domain 在后
不必把两者视为互斥选项。Cloudflare 支持在同一个 hostname 上让 Route 先执行,再将请求交给 Custom Domain Worker。
例如,api.example.com 的 Custom Domain 指向业务 Worker,而 api.example.com/auth 配了一条 Route,指向专门的鉴权 Worker。请求 /auth 时,鉴权 Worker 先校验、记录日志并限流。校验失败就返回;校验通过后调用 fetch(request),请求继续进入负责 origin 的业务 Worker。

图 2:/auth 先进入 Route Worker;失败请求在前置层返回,成功请求通过 fetch(request) 进入 Custom Domain Worker。
这种拆分适合把鉴权、审计、地域策略和请求规格化等横切逻辑留在前置 Route,把业务 API 留在 Custom Domain Worker。Route 的 pattern 应足够具体,并明确哪些分支终止、哪些分支继续 fetch(request)。否则出现问题时,很难判断响应究竟由哪一层产生。
Route 命中的请求,会算进每日请求量吗?
会。 Cloudflare 将 Workers 的计量对象定义为“入站到 Worker 的请求”,没有为 Route、Custom Domain 或 workers.dev 设置不同的请求额度。外部请求命中 Route 并实际执行 Worker,就是一次 Workers 入站请求;通过 Custom Domain 调用 Worker 也按同一口径计算。Route 不能绕开 Workers 的请求额度。
按本文核对时的官方文档,Workers Free 的额度是每个账户每天 100,000 次请求,在 UTC 00:00 重置。超出额度后会触发 Error 1027。对于 Route,账户可以选择达到上限后的行为:fail open 绕过 Worker,按未配置 Worker 的方式继续处理;fail closed 直接返回 1027 页面。
fetch(request) 回源时要区分两件事。客户端命中 Route 并运行前置 Worker,是一个入站 Workers request;Worker 再用 fetch() 请求传统源站或外部服务,则是一个 subrequest。Cloudflare 说明 subrequest 不另计入 Workers 请求账单,但它会出现在 Subrequests 指标中,并占用单次调用的 subrequest 配额。因此,普通的“Route → fetch(request) → 原站”不应理解为同一个客户端请求在 Workers 入站用量里算两遍。
若 Route Worker 再用 fetch(request) 调用同 hostname 的 Custom Domain Worker,下游 Worker 会实际执行。此类 Worker-to-Worker 链路应同时看各 Worker 的 Metrics 与 subrequest 指标;不能按“只有一层 Worker”估算 CPU、日志和内部调用负担。对于不应公开的内部服务,Service Binding 通常比经 Custom Domain hostname 串联更清楚。
| 场景 | 是否消耗一次外部 Workers 入站请求 | 还会产生什么 |
|---|---|---|
| 客户端命中 Route,Worker 直接返回响应 | 是 | Worker 执行时间与日志用量 |
客户端命中 Route,Worker 执行 fetch(request) 回传统源站 | 是 | 一次 origin-facing subrequest,受 subrequest 配额约束 |
| 客户端命中 Custom Domain Worker | 是 | Worker 执行时间与日志用量 |
| Route 未关联/未执行 Worker,或达到 fail-open 后绕过 Worker | 否 | 按非 Worker 请求路径处理 |
| Route Worker 调用 Service Binding | 初始客户端请求仍算一次 | 内部调用受运行时资源限制,且不经过公开 URL |
Paid 的 Standard 使用模型改为按月计量:每月包含 1,000 万次 Workers 请求,超过后按额外请求量计费。无论免费还是付费,原则都不变:请求经过并执行 Worker,才计入 Workers 用量。
同 zone 调用:能访问,不等于应该公开
同一个 zone 内,Worker 可以通过 fetch("https://api.example.com") 调用 Custom Domain Worker;Route 和 workers.dev 不能成为 same-zone fetch() 的目标。
不过,能通过 hostname 调用,不代表每个内部服务都应暴露为 HTTP 入口。鉴权、计费、规则计算等内部 Worker 更适合用 Service Binding:一个 Worker 直接调用另一个 Worker,不经过公开 URL。
实际分工可以很简单:对外 HTTP 接口用 Custom Domain;路径级前置策略用 Route;私有的 Worker-to-Worker 调用优先用 Service Binding。
DNS 和证书,常见问题在这里
Custom Domain 会自动创建 DNS record 并签发所需证书,但不能直接建在已有 CNAME record 的 hostname 上。删除 Custom Domain 后,关联的 Advanced Certificate 也不会自动删除,仍需在 SSL/TLS 的证书清单中处理。
Route 不负责建立 hostname 的解析。要让它生效,domain 或 subdomain 必须先有 DNS record,而且该 record 必须被 Cloudflare 代理。只加一条 Route、却没有可解析或未代理的 hostname,流量不会按预期进入 Worker。若目标是把 www.example.com CNAME 到 visa.cn 这类优选入口,也应继续用 Route 复用这条解析,而不是改成 Custom Domain。
| 常见问题 | Custom Domain | Route |
|---|---|---|
| 是否先手工准备 DNS | 通常不需要 | 需要 |
| hostname 已有 CNAME | 先处理冲突,不能直接创建 | 可以继续使用既有 DNS/origin |
| 删除后的清理 | 检查并清理遗留 Advanced Certificate | 按需删除 Route;DNS/origin 仍由原配置管理 |
| 容易忽略的点 | 根域和 www 是两个 hostname | DNS 必须被代理,pattern 不是正则 |
何时从“全域 Route + 占位 DNS”迁到 Custom Domain?
有些纯 Worker 应用会使用 example.com/* Route 加占位 CNAME/AAAA,只为让 hostname 能解析。如果 Worker 已是最终应用源站,可以评估迁到 Custom Domain。Cloudflare 建议的流程是:处理冲突 CNAME,添加 Custom Domain,再删除旧 Route。
迁移前先看代码里的 fetch(request)。如果它的含义是把请求原样送回旧站点,直接改为 Custom Domain 会改写整个请求链路;此时应继续使用 Route,或拆成“Route 前置 + Custom Domain 业务后端”。不能只因为看到 /*,就认为它等同于一个 Custom Domain。
快速选择
| 你的情况 | 建议 |
|---|---|
| 新建 API、BFF、Webhook,Worker 就是服务端 | 使用 Custom Domain |
| 有传统源站,只需对部分 URL 增加边缘逻辑 | 使用 Route |
| 要把 hostname CNAME 到优选入口,再绑定 Worker 改善国内访问 | 使用 Route,不要用 Custom Domain 接管 DNS |
| 需要路径级鉴权、限流、重写或分流 | 使用 Route |
| 需要在同一 hostname 上分离前置策略和业务 API | 组合 Route + Custom Domain |
| 需要私有的 Worker-to-Worker 调用 | 使用 Service Binding,而非公开 URL |
选型时不需要把两个功能分出高下。关键是把职责放对位置:Custom Domain 决定 Worker 应用怎样作为 hostname 对外提供服务;Route 决定哪些 URL 在到达源站前被截获;Service Binding 处理不需要公开的内部调用。
参考资料
Mttao GitHub ↗
探索技术与生活的智慧