主域名留在 Cloudflare,只把一个子域名委派给 DNSPod 做国内外分流

需求

一个子域名 sub.example.com,想要国内和海外走不同的线路:

  • 海外:走 Cloudflare CDN,回源到海外源站
  • 国内:走国内 CDN(我这里用腾讯云 EdgeOne),不过 CF

前提是:主域名 example.com 继续托管在 Cloudflare,其余所有记录一条都不动。只把这一个子域名的解析权单独挪出去。

全流程我做成了个小工具,不想手动走的可以直接跳到文末「顺手做了个工具」,或者看 splitdns 使用教程

为什么 Cloudflare 单独搞不定

两个坎:

  1. CF 权威 DNS 不支持普通记录按地区给不同答案。原生方案只有 Load Balancing 的 Geo Steering,付费附加组件。
  2. 记录一旦是橙云,CF 返回的就是 anycast IP,全球都一样,DNS 层面根本没得分。

所以只能把这一个主机名交给支持线路的 DNS 服务商(DNSPod)。但这就带出第二个坎:

委派出去之后,CF 就不再是这个主机名的权威 DNS 了,橙云、缓存、WAF、Universal SSL 对它全部失效。那海外线还怎么走 CF CDN?

答案是 Cloudflare for SaaS(自定义主机名)。它本来是给 SaaS 厂商代理客户自有域名用的,特点恰好是「DNS 不在 CF 也能让 CF 代理这个主机名」。Free 版就能用。

整体结构

example.com (Cloudflare)
    ├── www / mail / 其它所有记录 ──> 照旧,完全不受影响
    └── sub  NS ──> DNSPod            ← 只有这一条被委派出去
                      │
                      ├── 境内线 ──> CNAME sub.example.com.eo.dnse2.com   (EdgeOne)
                      └── 默认线 ──> CNAME fallback.mycdn.net (CF 橙云) ──> 海外源站

下面把 SaaS 用的第二个域名叫 mycdn.net、海外源站 IP 叫 203.0.113.10,自行替换。

需要三样:主域名在 CF、另一个也在 CF 的域名(做 SaaS 区)、一个 DNSPod 账号。自定义主机名不能和 SaaS 区同名,所以第二个域名是必须的。


第一步:配 Cloudflare for SaaS(在 mycdn.net 这个区做)

SSL/TLS → 自定义主机名,先启用 Cloudflare for SaaS。

1. 建回落源(Fallback Origin),必须是本区内的橙云记录:

Type Name Content Proxy
A fallback 203.0.113.10 Proxied

回到自定义主机名页面,把 fallback.mycdn.net 填进「回退源」,等状态变 有效。面板上那句提示不是摆设:回退源没到 Active,自定义主机名验证不会通过

2. 添加自定义主机名:填 sub.example.com,证书验证方式选 TXT

别选 HTTP 验证。HTTP 验证要求域名已经指向 CF 才能过,而这时你还没切 DNS,会变成先有鸡还是先有蛋。

添加完 CF 会给你几条 TXT,全部记下来:

  • _cf-custom-hostname.sub.example.com —— 主机名归属验证,一条
  • _acme-challenge.sub.example.com —— 证书 DCV,可能是两条(证书带通配符 SAN 时,基础域名和 *.sub.example.com 各要一条,主机记录同名、值不同,两条都得加)

签出来的证书是 CF 代签的证书(我这边签出来的有 SSL.com 也有 Google Trust Services),三个月有效期,到期自动续,不用管。

3.(可选)给单个主机名指定不同的源服务器

回退源是所有自定义主机名的默认去处。如果某个主机名要走另一台机器,可以在它的「编辑」里单独填「源服务器」和「源服务器 SNI 值」——这个功能现在 Free / Pro / Business 都能用了,以前是企业版专属。

:warning: 这里有个很坑的点:源服务器一旦不是默认回退源,那台机器必须能认得这个 SNI 名字,否则一律 403。

原因是 CF 回源时,Host 头是 sub.example.com,但 TLS 握手用的 SNI 是你填的「源服务器 SNI 值」(比如 ks-5.mycdn.net)。Traefik / Nginx 这类按 Host / SNI 路由的网关匹配不到对应的 router,就直接把请求打回来了。

解决办法很简单:在那台机器上给这个名字随便挂一个 router / vhost 就行,指向哪个服务、有没有 SSL 证书都无所谓,有这么个条目让它能路由到就够了。不加的话你会对着一个莫名其妙的 403 查半天。

另外注意:单独指定「源服务器 SNI 值」是企业版 SSL for SaaS 才开放的字段,非企业账号填了会被 1456 拒掉。但绝大多数情况根本不用填——CF 默认就拿源服务器主机名当回源 SNI,留空即可。

第二步:在 DNSPod 建区并配好记录(先建好,最后才委派

DNSPod 支持直接把子域名当独立域名添加:在「添加域名」处填 sub.example.com(免费版就行)。

如果主域名不在这个腾讯云账号下,DNSPod 会要求先加一条 TXT 证明归属(报 QuhuiTxtNotMatch)。那条 TXT 是加在主域名上的,也就是还在 CF 的 example.com

建好后进域名详情,看它分配给你的 NS(免费版一般是 f1g1ns1.dnspod.net / f1g1ns2.dnspod.net,付费版是 ns3.dnsv2.com 之类,以控制台实际显示的为准)。

:warning: 新加的域名默认是「暂停解析」状态,记得去域名列表里开启。不开的话委派、记录可以全对,解析就是不出结果,很难看出问题在哪。

加记录,主机记录都是相对 sub.example.com 这个区而言的:

主机记录 类型 线路 记录值
@ CNAME 默认 fallback.mycdn.net
@ CNAME 境内 sub.example.com.eo.dnse2.com(EdgeOne 给的 CNAME)
_cf-custom-hostname TXT 默认 CF 给的值
_acme-challenge TXT 默认 CF 的 DCV 值之一
_acme-challenge TXT 默认 CF 的 DCV 值之二(通配符 SAN 那条)

几个要点:

  • CF 那条放「默认」线,不要放「境外」线。 默认线是兜底,任何没匹配到境内的解析器都有答案;只配境内 + 境外的话,某些识别不了归属的解析器会拿不到记录。
  • 同名两条 _acme-challenge TXT 是正常的,都是 CF 要的,别以为加重复了删掉一条——少一条证书签不出来。EdgeOne 那边如果也要验证,是它自己的另一套记录。
  • DNSPod 免费版只有「默认 / 境内 / 境外」三条线路,没有国家级线路,要精确到某个国家得专业版以上。国内一套、海外一套的话免费版够用。
  • @ 上放 CNAME 是非标准用法,DNSPod 允许,但会和同名的其它记录类型冲突。如果它拒绝,见文末替代方案。
  • TTL 填 600 秒。DNSPod 免费版最低就是它,填更小会被接口直接拒掉。

国内线走 EdgeOne 的话,证书由 EO 托管,不用自己在源站上折腾——按 EO 自己的流程把域名加进去、走完它的验证就行。

第三步:在 Cloudflare 上做 NS 委派

回到 example.com 这个区。

1. 先处理掉 sub 以及 *.sub 下面所有已有记录。

别偷懒。留着不报错,但它们会变成 shadowed records——记录还在列表里看得见,实际一条都不生效。排查的时候能把人整疯。

注意是「处理」不是「直接删」:如果这些记录还在服务(比如 api.sub.example.com 指着某个后端),要先把它们搬到 DNSPod 的新区里去,再从 CF 删。直接删就把线上打断了。

2. DNS → 记录 → 添加记录,加两条 NS:

类型 名称 内容 TTL
NS sub f1g1ns1.dnspod.net 自动
NS sub f1g1ns2.dnspod.net 自动

代理状态显示「仅 DNS」是正常的,NS 类型本来就没有橙云开关。Free 版就能加 NS 记录做委派。

这一步只影响 sub 这一个名字example.com 的其它记录、NS、注册商那边的设置统统不用动。

顺带说一句:CF 面板里那个 Enterprise-only 的 Subdomain setup 是另一回事——那是把子域作为独立 zone 托管在 CF 自己这边。委派给第三方用不到它。

3. DNSSECexample.com 开了 DNSSEC 的话,子区不签名就不用管(正常的 insecure delegation);子区也签名了才需要在 CF 加一条 sub 的 DS 记录。

4. glue 记录:不需要。只有 NS 主机名本身在 sub.example.com 之下(比如 ns1.sub.example.com)才需要 glue。

第四步:验证

看委派有没有生效:

dig +trace sub.example.com

直接问 DNSPod 的 NS:

dig @f1g1ns1.dnspod.net sub.example.com

看证书和是否过 CF:

curl -sIv https://sub.example.com 2>&1 | grep -E 'subject|issuer|cf-ray'

最后回 CF 的自定义主机名页面,等到证书状态:有效主机名状态:有效。委派 + TXT 生效通常几分钟。


踩坑清单

委派之后,CF 对这个主机名不再有权威。 主域名区里的 WAF 规则、缓存规则、页面规则、Universal SSL 都不覆盖它了。海外线的 HTTPS 由自定义主机名的证书提供,走的是 mycdn.net 那个区的 SSL/TLS 设置和规则——要配规则去那个区配,别在主域名区里找。

自定义源服务器不是解析目标。 解析要指向 SaaS 区里的橙云记录(把流量带进 CF),源服务器是 CF 收到之后往哪转。写反了 CF 根本不认识那个名字。

用了自定义源服务器就去源站上补 SNI 对应的 router,见第一步第 3 点,不然只会看到 403。

自定义主机名的「最低 TLS 版本」默认是 TLS 1.0,介意的话记得手动调到 1.2。

海外源站的防火墙要放行 CF 回源 IP,SSL/TLS 模式在 mycdn.net 区里设 Full (strict)。

如果国内线不是走 CDN 而是直连源站,那台机器要自己持有 sub.example.com 的证书,而且 DNS-01 续期的 _acme-challenge TXT 得加在 DNSPod 而不是 CF,已有的自动续期脚本记得改 provider。

操作顺序别反:先建好 DNSPod 的区和全部记录,最后一步才在 CF 加 NS、删旧记录。这样切换瞬间就有解析,中间不断。


替代方案:不做委派

如果 DNSPod 的 @ CNAME 不好使,或者不想动 NS,还有更轻的做法:

主域名区里 sub 保留成灰云 CNAME,指向 sub.geo.另一个放在DNSPod的域名,把分线路配在那个名字上。

sub    CNAME  sub.geo.mydns.net    仅 DNS(灰云)

效果一样(GeoDNS 判定依据是递归解析器的位置 / EDNS Client Subnet,多一跳 CNAME 不影响),还绕开了 apex CNAME、DNSSEC、shadowed records 这一堆事,改回橙云就能回滚。

先试这个,真不行再上 NS 委派。


顺手做了个工具

上面这套流程环节多、每步都要等生效,域名一多就靠人记不住,于是做成了工具:

Windows 与 macOS 桌面版:

  • Windows 安装版 —— 数据在 %APPDATA%\splitdns,升级重装不动它
  • Windows 绿色版 —— 解压即用,数据在 exe 同级 data/,整个文件夹拷走就带走全部配置
  • macOS —— universal,Intel 与 Apple Silicon 通用,数据在 ~/Library/Application Support/splitdns;没做签名公证,首次打开右键 → 打开,或 xattr -dr com.apple.quarantine splitdns.app

它干这几件事:

配置向导——把上面四步拆成带验证的步骤。能自动做的直接调 API(建回退源、加自定义主机名、建 DNSPod 域名并启用解析、写 DCV TXT、配分线路记录、加 NS 委派),做不了的给出精确到「填什么值」的指令。关键是平台接口返回成功不算数,巡检查到真的生效才算完成——所以关掉程序隔天回来进度还在,会按当前真实状态重新判定;已经通过的步骤如果线上被人改了会自动退回「等待生效」并说明差在哪。

巡检——随时核对三个平台的实际状态,专门盯这些容易翻车的地方:

  • 父区里被委派遮蔽的记录(那些看得见但不生效的)
  • CF 要的验证 TXT 少没少(通配符 SAN 那两条同名不同值最容易漏)
  • CF 那条有没有挂在「默认」线兜底
  • DNSPod 上域名是不是还处于暂停状态
  • 委派的 NS 与 DNSPod 实际分配的对不对得上
  • 证书状态、到期时间、自定义源服务器是否与配置一致

清理那一步会先迁移——被遮蔽不等于该扔。它逐条判断:接入域名本身只删不搬(落点归线路配置管),其余原样搬去 DNSPod 再删,橙云记录会单独提示「搬过去就是直连了」。

不联网上传任何东西,密钥只存在本机,程序也不监听端口。需要自备 Cloudflare API Token(Zone:Read + DNS:Edit + SSL:Edit)和腾讯云 SecretId / SecretKey。