DNS 中的 CNAME 记录:限制与应用实践
注意:本人并非专业运维人员,对 DNS 相关技术的了解并不全面。本文由 ChatGPT 核查未发现错误,但不代表一定正确。欢迎指正。
今天在做某个业务时,才发现 CNAME 和 MX 记录不能共存,进一步查询后,发现 CNAME 记录不能和其他任何记录共存。具体原因是 RFC 的相关定义:
- RFC 1034, Section 3.6.2: If a CNAME RR is present at a node, no other data should be present; this ensures that the data for a canonical name and its aliases cannot be different.
- RFC 1912, Section 2.4: A CNAME record is not allowed to coexist with any other data. In other words, if
foo.example.com
is defined as a CNAME pointing tobar.example.com
, you can't also have an MX record, or an A record, or a TXT record forfoo.example.com
. This rule also applies to the root domain; you cannot have a CNAME record at the same label as other record types.
在 RFC 1034 的第 3.6.2 节中,明确说明了 CNAME 记录不能与其他记录共存,RFC 1912 第 2.4 节进一步强调了这一点。我并没有找到禁止裸域名(例如 yipai.me 就是裸域名,www.yipai.me 则是该域名的子域名)添加 CNAME 记录的 RFC 标准文件,所以从纯技术上讲,给裸域名添加 CNAME 记录也是符合标准的。但现实中,裸域名可能存在 MX、NS 或 SOA 等等记录,根据 RFC 规范,CNAME 不能和这些共存。这也是为什么很多 DNS 服务不能在裸域名上添加 CNAME 记录的原因。
如果最初没规划好,使用 domain.tld 作为邮箱后缀,并使用 domain.tld 做网站主域名(A 记录解析到 IP);后续想用 domain.tld 作为网站业务域名并接入 CDN 时,就会遇到麻烦。因为 CDN 一般是通过 CNAME 方式接入的,而 domain.tld 已经有了 MX 记录并且已经用了很久,有很多外部联系人,不方便切换到 mail.domain.tld 。
如果你的域名 DNS 服务器是使用的 Cloudflare,那就不存在此问题。因为 Cloudflare 有 CNAME Flattening 功能,会解析并自动缓存你的 CNAME 最终目标 IP,看上去添加的是 CNAME 记录,实际上设置的是 A 记录(也有可能并没有真的设置只是接口返回,但因为是黑盒,具体怎么处理的不得而知,用户感知上也无差异)。具体可见:CNAME flattening。类似 CNAME Flattening 的技术其他国外的厂商也有支持的,可能叫 ANAME。
而国内的厂商,似乎并没有类似的功能。比如 DNSPOD,有一个叫 CNAME 加速的功能,根据其介绍,这个功能并不是直接返回 CNAME 递归查询后得到的 A 记录的值,而是同时返回 CNAME 记录和 A 记录,似乎并不符合规范。因为我没有 DNSPOD 账户,所以也就不测试其真实返回值情况了。其他厂商应该也不行,否则不会有这个项目。
但这个项目因为是真的去解析 CNAME 记录的目标值,然后利用 DNS 服务商的 API 去更新 A 记录,所以存在不稳定性。因为我们无法得知 CDN 的域名解析记录更新时间,尽管 CDN 的 IP 变动并不频繁,但依然会有时间差,对于稳定性要求高的企业来说是不可接受的。
所以裸域名在有 MX 记录的情况下接入 CDN 并保证稳定性是不太可能的了。那如果说场景不是裸域名接入 CDN,而只是将裸域名跳转到 www 域名或其他子域名上,是否可行呢?
你第一时间是否想到了 DNS 服务商常有的 URL 转发功能?尽管 URL 转发并不在 DNS 标准中,但服务商如果提供了此功能,能实现转发也行。但很可惜不行,根据 DNSPOD 相关文档,URL 转发也是和 MX 记录冲突的。我不明白为什么会冲突,因为我推测 URL 转发只是在 DNSPOD 的某台 NGINX 服务器上新增一个 301/302 跳转配置,DNSPOD 给域名添加 A 记录(或模拟返回 A 记录)到 DNSPOD 到这台服务器 IP 。就算接入了 CDN,DNSPOD 自己也知道 CDN 的最新 IP,除非他不是自建的 CDN 或者不使用的亲爹腾讯云 的 CDN。但这种可能性几乎不存在,所以我难以理解。
那对于我们想要将有 MX 记录的裸域名跳转到 www 或其他子域名,就只剩一种可能的方式:裸域名直接添加 A 记录。添加 A 记录有两种方式:
- 直接解析到源站。在源站的 NGINX 配置中增加 301/302 跳转。
- 解析到单台 NGINX 服务器,在这台 NGINX 上配置跳转。
如果源站是静态站点,使用两种方式区别不大。但出于安全考虑,不想暴露源站的 IP 地址,那还是采用方案 2 更好。但使用 A 记录解析,无法做全球加速,尽管只是做跳转的 NGINX 服务器并没有什么压力,但至少全球其他区域对大陆的网络连接质量还是不太理想的。为保证服务质量,还得多个 NGINX 服务器做分地区解析,还好 DNSPOD 是有这个功能的。
综合起来看,因为缺少 DNS Flattening 技术,在国内裸域名接入 CDN 技术是不可能的,做跳转是有可能的。
dnspod 是可以针对范解析直接添加 cname 的,虽然提示冲突,直接忽略就行了。
我就是这么干的。
国外很多 dns 服务商提供了其他的解决方案,例如 he.net 是 alias 纪录。
url 跳转这个不是正常的 dns 行为,并且稳定性说实话,比较一般。我用的 dnspod 的跳转,跳转速度一般。有时候还会失败。
会丢邮件,个人可能不太敏感,但对几十万人的企业来说,丢邮件就麻烦了。而且,非标准操作,出了问题自己要背锅,没必要。
CF 使用中,但从大陆访问优质 CN2 线路的主机,CF 代理还不如直连速度快。唉,安全和性能不可兼得。
国情如此。