Skip to content

换绑、退役、并行:域名变更的正确顺序 ​

给一个服务换域名,看起来是"改一条 DNS 记录"的事。但在隧道/反向代理这套结构里,实际要动的是四层:

  1. DNS 记录(域名指向哪)
  2. 隧道的 ingress 规则(哪个域名转发到哪个本地端口)
  3. 应用侧的凭据绑定(有的隧道应用靠 appName 关联)
  4. 目标服务自己的 Host 白名单 / 站点 URL(不改会 400 或重定向到旧域名)

漏掉任何一层,症状都是"域名能解析、但打不开",而且每一层的报错都不一样。这篇是这套操作里我固定下来的顺序。

先搞清楚:你要做的是哪一种 ​

  • 换绑:服务不变,域名换一个(old.example.com → new.example.com)
  • 退役:域名彻底不要了,服务留或删
  • 并行:两个域名同时指向同一个服务(迁移期用,最安全)

并行最安全的理由很实际:新旧两个域名同时可用,你可以慢慢改客户端/书签,出问题随时回退。所以下面所有流程我都是在"并行"状态下做的。

顺序(我实际用的) ​

第 1 步:加新域名的 ingress 规则,不动旧的 ​

在隧道配置里追加一条新规则,旧规则保持原样:

json
{
  "ingress": [
    { "hostname": "new.example.com", "service": "http://localhost:3000" },
    { "hostname": "old.example.com", "service": "http://localhost:3000" },
    { "service": "http_status:404" }
  ]
}

⚠️ 大多数隧道配置接口是整体覆盖语义,不是"增量追加"。所以你要先把现有配置读出来,追加一条,再整体写回去。

bash
# 先读
curl -s -H "Authorization: Bearer $CF_TOKEN" \
  "https://api.cloudflare.com/client/v4/accounts/$ACC/cfd_tunnel/$TID/configurations" \
  | jq '.result.config'

读不出来就别写。 只看界面截图去手写一份 ingress,是最容易把别的服务搞挂的做法——我见过一次"改一条规则,结果三个域名同时 502"。

第 2 步:加 DNS 记录 ​

新域名要有一条指向隧道的 CNAME:

new.example.com  CNAME  <tunnel-id>.cfargotunnel.com  (proxied)

如果隧道应用有"注册应用域名"这类接口,通常会顺带建这条记录。但要验证,不要假设。

第 3 步:等生效,然后用 DoH 和 --resolve 双重验证 ​

刚建的记录,本机查经常是 NXDOMAIN —— 那是上游 DNS 的负缓存(它记住了"这个域名不存在"一段时间)。这不代表记录没建好。

bash
# 用公共 DoH 查真实结果
curl -s "https://doh.pub/dns-query?name=new.example.com&type=A" \
  -H 'accept: application/dns-json'

# 或者跳过 DNS,直接指定 IP 测
curl --resolve new.example.com:443:203.0.113.10 https://new.example.com/ -o /dev/null -w '%{http_code}\n'

两层都过了,才算"域名真的通了"。

第 4 步:改目标服务自己的配置 ​

这一步最容易被忘。服务通常维护着自己的"我是谁":

  • 站点 URL(WordPress 的 home/siteurl、笔记应用的 APP_URL)
  • 允许的 Host 头 / CORS 白名单
  • cookie 的 domain

不改的表现:页面能打开,但所有链接还指向旧域名;或者登录状态在跳转后失效;或者直接 400 Bad Request(Host 不匹配)。

第 5 步:旧域名留够时间,再退役 ​

退役顺序反过来:先删 ingress → 再删 DNS → 最后删凭据。

原因是:先删 DNS 的话,如果 ingress 还在,会有人用旧域名访问到"能解析但连不上";反过来先删 ingress(旧域名命中兜底 404),至少错误是干净的。

⚠️ DNS 记录不一定能通过 API 删。我用的那套隧道应用就没有"删除 DNS 记录"的接口(试了几个常见端点全 404),残留的 CNAME 只能去 Cloudflare 控制台手删。

⚠️ 凭据/绑定关系里也会留残影。比如 app_domains.json 里可能还留着已删除 appName 的条目。这类残影一般无害(不参与路由),别顺手去清理,容易连带把现役的删掉。

三个反复出现的坑 ​

1. 一个 appName 只能绑一个域名

有的隧道应用用"应用名"做凭据维度,一个 appName 只能对应一个域名。想加第二个域名必须新建凭据,而这类应用往往把凭据缓存在内存里 —— 新建完不重启不生效。

细节见:Cloudflare Tunnel 的凭据缓存坑

2. 免费二级域名不能 CNAME 到隧道

某些免费二级域名服务只允许 A 记录,不允许 CNAME。而隧道需要的是 cfargotunnel.com 的 CNAME。这种情况下只能在那边直接写 A 记录指向 Cloudflare 的 IP —— 这又绕开了 tunnel 的路由匹配,实际是"直连 CF 边缘再回落",能不能通取决于那条链路。

结论:免费域名适合做"绕开 CF 的备用入口",不适合做隧道域名。

3. 退役一个域名之后,别忘了还有谁在用

书签、客户端配置、OAuth 回调地址、Webhook 地址、别人的收藏。退役前在日志里查一下旧域名最近有没有访问:

bash
grep -c "old.example.com" /var/log/apps/<app>.log

检查清单 ​

换绑/并行:

  1. 读现有 ingress → 追加新规则 → 整体写回(别手写全量)
  2. 建 DNS(CNAME → cfargotunnel.com,proxied)
  3. DoH + --resolve 双验证
  4. 改服务侧的站点 URL / Host 白名单 / cookie domain
  5. 新旧域名并行跑一段时间

退役:

  1. 日志确认没有访问
  2. 删 ingress → 删 DNS(可能得手删)→ 删凭据
  3. 别去清理配置里的历史残影

一句话版本:域名变更真正的风险不在 DNS,在整体覆盖式的 ingress 写回。先读、追加、再写,永远不要在看不到现有配置的情况下改隧道规则。