在一篇题为《先有 CNAME,还是先有 A 记录?》的最新文章中,Cloudflare 解释了一个 RFC 规范表述不清的问题,是如何导致其广受欢迎的 1.1.1.1 公共 DNS 服务发生故障的。在定位问题并发现旧版 DNS 标准中关于记录顺序的模糊之处后,Cloudflare 提出了一份澄清后的规范建议。
1 月 8 日,一次看似例行的 DNS 服务更新改变了响应中 CNAME 记录出现的顺序,导致部分 DNS 客户端在解析域名时失败,因为它们假定别名记录必须先出现。尽管大多数现代软件认为 DNS 响应中记录的顺序并不重要,但 Cloudflare 团队发现,一些实现实际上依赖 CNAME 记录必须出现在其他记录类型之前。
当这一顺序发生变化后,DNS 解析开始失败,最终引发了 1.1.1.1 这一流行公共 DNS 服务的一次严重中断。Cloudflare 的系统工程师 Sebastiaan Neuteboom 解释了该变更引入的原因和时间点:
在对缓存实现进行一些降低内存占用的改进时,我们引入了一个关于 CNAME 记录顺序的细微变化。该变更于 2025 年 12 月 2 日引入,12 月 10 日发布到测试环境,并于 2026 年 1 月 7 日开始部署。
当 DNS 解析器查询一个包含 CNAME 的域名时,它可能会看到一系列别名记录,将原始名称一路链接到最终的地址,并且解析器会以不同的过期时间缓存链路中的每一步。Cloudflare 指出,如果这条链中的某一部分在缓存中已过期,解析器只会重新获取过期的那一段,并与仍然有效的部分组合,形成完整的响应。Neuteboom 补充道:
之前的代码会创建一个新的列表,先插入已有的 CNAME 链,然后再追加新获取的记录(……)。但为了减少内存分配和拷贝,代码被修改为直接把 CNAME 追加到现有的 answer 列表中。结果是,1.1.1.1 返回的响应中,CNAME 记录有时会出现在最底部,也就是最终解析结果之后。
示例如下:
;; QUESTION SECTION:;; www.example.com. IN A;; ANSWER SECTION:cdn.example.com. 300 IN A 198.51.100.1www.example.com. 3600 IN CNAME cdn.example.com.
虽然许多 DNS 客户端实现并不依赖记录顺序,例如 systemd-resolved,但也有一些实现(包括 glibc 中的 getaddrinfo 函数)在解析过程中会跟踪“期望的记录名称”,并按顺序遍历响应内容,假定在任何最终答案之前都能先遇到 CNAME 记录。Reddit 上有用户评论道:
一方面,我非常敬佩他们在事后分析中展现出的细节和极高的工程标准;但另一方面,我也忍不住觉得,他们似乎并没有建立起足够完善的测试体系(以及相应的工程文化),来真正理解他们的改动在全球范围内会产生怎样的影响。
在 Hacker News 上的一篇热门讨论中,许多用户围绕 RFC 是否真的存在歧义展开了争论,尤其是在 RRset 与 RR 在消息分区中的细微区别上,还是说 Cloudflare 的工程师误解了规范。Patrick May 则评论道:
这是一个典型的 Hyrum 定律案例:“当一个 API 拥有足够多的用户时,你在契约中承诺了什么并不重要,系统中所有可观察到的行为,都会被某些人所依赖。”再叠加上未能遵循 Postel 定律:“发送时要保守,接收时要宽容。”
在一份即将在 IETF 讨论的 Internet-Draft 中,Cloudflare 提议制定一份 RFC,明确规定 DNS 响应中应如何正确处理 CNAME 记录。
根据公开的时间线,Cloudflare 于 1 月 7 日开始全球部署,并在 1 月 8 日 17:40(UTC)覆盖了 90% 的服务器。公司随后宣布发生事故,并于 1 月 8 日 18:27 开始回滚变更,最终在 19:55 完成回滚。
原文链接:
https://www.infoq.com/news/2026/02/cname-rfc-cloudflare-outage/





