你的 SPF 记录看起来没有问题:为邮箱服务商、邮件群发工具和 CRM 各加了几条 include,最后以 -all 结尾。可邮件头里却出现 spf=permerror,DMARC 报告显示 SPF 出错,或者检测工具提示“too many DNS lookups”。原因通常是 SPF 标准规定最多只能有 10 个触发 DNS 查询的机制,而且每个 include 内部隐藏的查询也要计入。
本文说明哪些机制计入、哪些不计入,void 查询的独立上限,如何手工统计总数,如何在不丢失正常邮件的前提下降到 10 以内,为什么展平需要谨慎,以及为什么一个名称只能有一条 SPF 记录。文中的域名和 IP 地址均为示例。
本指南的适用范围
免费的上线准备快照只读取你输入的确切主机名上的 SPF 记录,并只统计该记录中会触发 DNS 查询的机制。它不会展开 include: 或 redirect=,不统计 void 查询,不发送邮件,也不是渗透测试。
“too many DNS lookups”是什么意思
接收服务器检查 SPF 时,会读取信封发件人域名(即 MAIL FROM 地址,后来显示为 Return-Path)上以 v=spf1 开头的 TXT 记录,并从左到右依次评估其中的机制。部分机制会让接收方再次发起 DNS 查询。RFC 7208 第 4.6.4 节规定,每次检查最多允许 10 个此类机制,包括你所引用的每条记录内部的机制。如果评估需要第 11 个,接收方必须停止并返回 permerror,表示已发布的记录无法被正确解析。
通常可以在三个地方发现这个错误:已投递邮件的 Authentication-Results 头(RFC 8601),例如 spf=permerror smtp.mailfrom=example.com;提到查询过多的退信;或者 DMARC 聚合报告中的 SPF 错误。
有两个细节容易让人困惑。接收方在评估过程中累计查询次数,并在第一个匹配的机制处停止。因此排在前面的服务发出的邮件仍可能通过,而 include 位于第 10 次查询之后的服务则会得到 permerror,问题看起来时有时无。此外,permerror 不同于 fail:SPF 只是不再起作用。只要 SPF 或 DKIM 中有一项通过,且域名与 From 地址对齐,DMARC 就能通过(RFC 9989),所以带有对齐 DKIM 签名的邮件仍能通过,只依赖 SPF 的邮件则不能。除此之外,接收方如何处理 permerror 由其自行决定。
哪些 SPF 机制计入上限,哪些不计入
| 机制 | 计入 10 次上限 | 其他需要了解的内容 |
|---|---|---|
include: | 1 次,加上被引用记录中的所有查询 | 如果被引用的名称没有 SPF 记录,结果为 permerror |
a、a: | 1 次 | 查询该名称的 A 或 AAAA 记录,取决于发件方通过 IPv4 还是 IPv6 连接 |
mx、mx: | 1 次 | 对返回的 MX 主机做地址查询另有独立的 10 次上限 |
ptr | 1 次 | RFC 7208 指出不应发布(SHOULD NOT) |
exists: | 1 次 | 如果构造出的名称有 A 记录则匹配;常与宏配合使用 |
redirect= | 1 次,加上目标记录中的所有查询 | 记录中含有 all 机制时会被忽略 |
ip4:、ip6: | 0 次 | 直接比较地址,无需查询 |
all | 0 次 | 为之前未匹配的所有发件方设置结果 |
exp= | 0 次 | 仅在结果为 fail 之后才查询,用于获取说明文本 |
你自己的 v=spf1 记录 | 0 次 | 第一次 TXT 查询不属于机制 |
这个额度属于整个评估过程,而不是某一条记录。一个 include 本身消耗一次查询,再加上其背后记录消耗的查询,一直计算到最深的嵌套层级。服务商会修改自己的记录,例如把地址段分散到更多层嵌套 include 中,所以即使你什么都没改,总数也可能变化。
设置这个上限,是因为 SPF 记录会让接收方代表发布者发出 DNS 查询;RFC 7208 第 11.1 节说明,否则攻击者可能借此向第三方的 DNS 发起洪泛攻击。RFC 还建议为整个检查设置至少 20 秒的时间限制。超时会得到临时错误 temperror,而不是 permerror。
第二个上限:void 查询不得超过两次
RFC 7208 还为返回空结果的查询设置了单独的上限:没有任何记录的应答(NOERROR 且应答数为零),或者名称错误(NXDOMAIN,即名称不存在)。这类查询称为 void 查询。实现应当(SHOULD)最多允许两次,超过同样会得到 permerror,即使总数仍低于 10。这样可以避免 SPF 记录让接收方去查询一长串不存在的名称。
void 查询通常来自遗留配置:
- 旧服务器的 DNS 名称被删除后,仍保留的
a:old-web.example.com。 - 对没有 MX 记录的名称使用
mx或mx:example.org。该机制不得回退到该名称的 A 记录,因此什么也查不到。 - 网站迁移到只响应
www的主机后,裸域名上仍保留的a。
include: 的要求更严格。如果被引用的名称不存在或没有发布 SPF 记录,该 include 不只是算一次 void 查询,而是直接让整体结果变成 permerror(第 5.2 节)。当你已停用的服务商注销了你的记录仍在引用的域名时,就会发生这种情况。请查询记录中指向的每一个名称;查不到结果的名称应当修复或删除。
如何手工统计 SPF 查询次数
从信封发件人域名开始,可以在你所发邮件的 Return-Path 头中看到,然后递归地逐层检查记录:
- 读取记录:使用
dig +short TXT example.com,Windows 下使用nslookup -type=TXT example.com。 - 每个
include、a、mx、ptr、exists和redirect记 1 次;ip4、ip6和all记 0 次。 - 对每个 include 和 redirect,读取其指向的记录并按同样方法计分,嵌套多深就计算多深。
- 把所有结果相加,并记下任何没有返回记录的名称。
下面是一条假设的记录,表面上只有五个会触发 DNS 查询的机制:
example.com. TXT "v=spf1 mx include:_spf.mail.example.net include:spf.crm.example.org include:news.example.net a -all"| 机制 | 查询次数 | 原因 |
|---|---|---|
mx | 1 | 对 example.com 的 MX 查询 |
include:_spf.mail.example.net | 4 | include 本身 1 次,该记录中的三个嵌套 include 共 3 次 |
include:spf.crm.example.org | 2 | include 本身 1 次,其中的 a: 机制 1 次 |
include:news.example.net | 3 | include 本身 1 次,一个嵌套 include 1 次,其中的 exists: 机制 1 次 |
a | 1 | 为旧网站服务器遗留的条目 |
-all | 0 | 无需查询 |
| 合计 | 11 | 超出上限 1 次 |
这个总数是最坏情况。匹配第一个 include 的发件方最多需要五次查询,可以通过。而不匹配任何前面机制的发件方会走到最后的 a,即第 11 次查询,于是伪造邮件得到的是 permerror,而不是 -all 本来要产生的 fail。每次新增发信服务时都要重新统计,平时也应定期检查,因为被引用的记录会在没有通知的情况下变化。
如何在不丢失邮件的前提下降到 10 次以内
- 删除不再使用的 include。例如以前的邮箱服务商、已取消的群发工具、CRM 试用服务。DMARC 聚合报告会显示哪些来源确实在以你的域名发信,帮助你发现无人需要的 include。删除前先与各服务的负责人确认。
- 检查某个 include 是否真的会被评估。SPF 是针对信封发件人域名检查的。如果某服务发出的邮件在
Return-Path中显示的是它自己的域名,接收方检查的就是该域名的 SPF 记录,而不是你的,你的 include 对这些邮件毫无作用。移除前请先对照服务商文档确认。 - 对自己控制的服务器,用
ip4和ip6代替a和mx。如果你自己的服务器从 192.0.2.10 发信,ip4:192.0.2.10不消耗查询,而a要消耗一次。还要想一想mx是否真的需要:接收邮件的服务器不一定负责发送,如果它们属于你的邮箱服务商,对方的 include 可能已经列出了其发信服务器。服务器迁移时记得更新地址。 - 为每个高发信量的发件方分配独立子域名。让群发或工单服务使用
news.example.com这样的退信域名作为信封发件人,它有自己的 SPF 记录和独立的 10 次上限。SPF 不会继承,所以子域名需要有这条记录。在 DMARC 默认的宽松对齐下,news.example.com仍与 example.com 的 From 地址对齐。许多服务把这称为自定义 return-path 或退信域名。 - 去掉
ptr。RFC 7208 指出不应发布它:它速度慢,依赖由连接 IP 地址所有者控制的反向 DNS,而且会消耗查询次数。请用ip4/ip6或服务商的 include 替代。 - 不要把调整顺序当作修复。把最繁忙的发件方移到最前面,意味着走到第 11 次查询的邮件变少,但记录对后面所有内容依然是坏的,包括本应命中
-all的伪造邮件。
DKIM 同样能减轻压力。DMARC 只需要一项对齐的通过,而且 DKIM 通常在转发后仍然有效,SPF 则不然。请确保每个服务都用你的域名签名,然后再精简 SPF 记录。
SPF 展平:查询更少,维护更多
展平是把一个 include 替换成它当前解析出的 ip4 和 ip6 地址段。查询次数会下降,但你的记录里现在保存了别人名单的副本,而这份副本不会自动更新。
| 风险 | 后果 | 如何控制 |
|---|---|---|
| 服务商新增或更改地址段 | 来自新地址的邮件无法通过 SPF,而且不会有任何提醒 | 只展平发布稳定且有文档说明的地址段的服务商,并按计划定期复查 |
| 服务商不再使用某些地址段 | 对方已放弃、之后可能被他人使用的地址,仍在你的记录中处于授权状态 | 删除服务商不再列出的地址段 |
| 记录变长 | RFC 7208 建议将 SPF 应答控制在 512 字节以内;在防火墙干扰 DNS over TCP 或 EDNS0 的环境中,超出单个 UDP 数据包的应答可能被悄悄忽略 | 只展平开销最大的少数几个 include |
| 基础设施经常变化 | 大型云平台会轮换发信地址 | 保留它们的 include;例如 Microsoft 建议不要展平其 Microsoft 365 的 include |
自动展平服务会按计划解析 include 并重写你的记录。这能解决地址段过期的问题,但让第三方介入了你的 DNS,而且更新失败会破坏你的 SPF。请先尝试其他办法。如果确实要展平,请记录替换了哪个 include 以及时间,定期与服务商发布的地址段比对,并在每次更改后测试 SPF(Microsoft Learn 对其客户给出的也是同样的建议)。
每个名称只能有一条 SPF 记录,拆分要拆对
一个名称必须恰好有一条以 v=spf1 开头的 TXT 记录。如果有两条,接收方无法选择,会返回 permerror(RFC 7208 第 4.5 节)。常见情况是新服务的设置指南写着“添加这条 TXT 记录”,而有人新增了第二条 SPF 记录,而不是修改第一条。所以第二条记录不会让额度翻倍,请把两条合并:
# 错误:同一个名称上有两条 SPF 记录
example.com. TXT "v=spf1 include:_spf.mail.example.net -all"
example.com. TXT "v=spf1 include:spf.crm.example.org ~all"
# 正确:只有一条记录
example.com. TXT "v=spf1 include:_spf.mail.example.net include:spf.crm.example.org -all"同一名称上的其他 TXT 记录,例如网站验证令牌,没有问题;只有以 v=spf1 开头的记录才算数。TXT 记录中的单个字符串最多 255 个字符。更长的 SPF 记录仍然是一条 TXT 记录,由多个带引号的字符串组成,接收方会不加空格地把它们拼接起来(第 3.3 节),所以在某个机制结束的位置,要用空格结束该字符串:
example.com. TXT "v=spf1 ip4:192.0.2.0/24 ip6:2001:db8::/32 include:_spf.mail.example.net " "include:spf.crm.example.org -all"其他几种错误也会产生同样的 permerror:记录中任何位置的语法错误,例如把 include: 写成 include=(第 4.6 节),或者 include 了一个没有 SPF 记录的域名。SPF 记录同样不会继承,所以每个用作信封发件人的名称都需要自己的记录,并且各自有 10 次的上限。
免费 SPF 和 DMARC 检测工具统计什么
免费的上线准备快照无需注册,对一个公开域名运行八项被动检查,其中一项读取邮件相关的 DNS 记录。它不是渗透测试,也不是六个领域的审计,并且不会发送邮件。
- 确切的主机名。它读取你输入的名称上的 SPF 记录,不会回退到上级域名。请输入信封发件人域名本身,例如 example.com,而不是 www.example.com。
- 顶层计数。它统计该记录中会触发 DNS 查询的机制(
include、a、mx、ptr、exists和redirect),超过十个时标记为中等风险信号。 - 不展开。它不会展开
include:或redirect=,不统计 void 查询,也不会标记同一名称上的第二条 SPF 记录。上面那个五个机制的例子能通过这项计数,尽管接收方需要 11 次查询,所以请同时自己做递归统计。 - 其他 SPF 信号。
+all被标记为高风险,?all为低风险;当主机有 MX 记录而缺少 SPF 或 DMARC 记录时,也会被标记。
如果想先查看邮件 DNS 检查,请使用免费的 SPF 和 DMARC 检测工具:它运行同一个快照,并把这项检查显示在其他七项之前。至于记录的其余部分,从 ~all 与 -all 的选择到把 DMARC 从 none 提升到 reject,请参阅 SPF 和 DMARC 指南。
常见问题
“SPF permerror: too many DNS lookups”是什么意思?
评估你的 SPF 记录需要超过 10 个会触发 DNS 查询的机制,包括被引用记录内部的机制。RFC 7208 要求接收方此时停止并返回 permerror,因此 SPF 无法再帮助邮件通过 DMARC。
ip4 和 ip6 会计入 SPF 查询上限吗?
不会。ip4、ip6 和 all 不需要 DNS 查询,也不计数。include、a、mx、ptr、exists 和 redirect 各计 1 次,而 include 或 redirect 还会加上其所指向记录中的所有查询。
SPF 的上限是 10 个 include 还是 10 次查询?
是 10 次查询。如果被引用的记录自身又包含 include、a 或 mx 机制,一条只有四个 include 的记录也可能超限。请递归统计每个 include 和 redirect,而不只是看得见的机制。
可以发布第二条 SPF 记录来获得更多查询次数吗?
不可以。同一名称上有两条以 v=spf1 开头的 TXT 记录,结果就是 permerror。请把它们合并成一条,或者把某个发件方迁到独立的子域名,该子域名有自己的记录和独立的 10 次上限。
SPF 展平能解决 10 次查询上限吗?
它能降低计数,但复制来的 IP 地址段会在服务商更改后过期,来自新地址的正常邮件随后会无法通过 SPF。请先删除无用的 include 并使用子域名;只展平地址段稳定且有文档说明的服务商,并定期复查。
如果 SPF 记录返回 permerror,DMARC 一定会失败吗?
不一定。DMARC 只需要一项对齐的通过,所以带有与 From 域名对齐的有效 DKIM 签名的邮件仍能通过。只依赖 SPF 的邮件则不行,这正是修复记录的意义所在。
来源与延伸阅读
- RFC 7208:发件人策略框架(SPF)www.rfc-editor.org
- RFC 7208 第 4.6.4 节:DNS 查询限制www.rfc-editor.org
- RFC 9989:DMARCwww.rfc-editor.org
- RFC 8601:用于标示邮件认证状态的邮件头字段www.rfc-editor.org
- Microsoft Learn:为 Microsoft 365 域名设置 SPF 以识别有效邮件来源learn.microsoft.com
由 Sitelemetry 编辑团队编写。文末的一手资料可帮助你深入了解相关主题。



