KernelRanch
返回文章列表

SPF / DKIM / DMARC:三条记录各管什么

2026-11-10 #域名

邮件进垃圾箱,九成是这三条记录的问题。它们常被当成一组配置一起抄,实际职责完全不同:SPF 说"哪些服务器可以代表我的域发信",DKIM 说"这封信确实是我发的且没被改过",DMARC 说"前两项没通过时怎么处理,报告发给我"。读完这篇,你能给一个域配齐三条记录,也能读懂别人域上的值在表达什么。

收信侧(把信转到你的邮箱)是另一套配置,见 88-email-routing.md。记录写在 Cloudflare 的 DNS 面板里,写法参考 50-dns-records-tutorial.md。

1. 三条记录的分工

记录位置作用谁在读
SPF根域 TXT,v=spf1 开头列出允许发信的 IP 与网段收信方在 SMTP 阶段查信封域
DKIM<selector>._domainkey 的 TXT存放公钥,验证签名收信方验证信头与正文哈希
DMARC_dmarc 的 TXT对齐判定、失败策略、报表地址收信方结合前两项做最终判定

关键差别在"校验对象"。SPF 校验的是信封发件人(Return-Path),用户在客户端看到的 From 不一定被它覆盖;DKIM 签的是信头与正文,与转发路径无关,只要签名没被破坏就一直有效;DMARC 要求 SPF 或 DKIM 至少一个通过、且与 From 域对齐,才判为通过。

2. SPF:一条 TXT 加十次查询上限

最简写法是 v=spf1 include:_spf.example.net ~all,~all 表示其余来源标为 softfail(进垃圾箱但不拒收),-all 表示直接失败。域名多、服务商多的时候,SPF 会变成一串 include。

Google 的根域用的是 redirect 而不是 include,本机实查:

gmail.com TXT: v=spf1 redirect=_spf.google.com

redirect 表示"我的策略全部在另一个域里",展开后:

_spf.google.com TXT: v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17
  ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56
  ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56
  ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all

这里有个硬限制:SPF 求值时最多做 10 次 DNS 查询,每个 include、a、mx、ptr、redirect 都算一次,超了直接返回 permerror,等于 SPF 全废。接了多个 SaaS(邮件营销、工单、CRM 各给一条 include)很容易撞线。撞线之后的处理只有两条路:砍掉不用的服务商,或者把多条 include 扁平化成具体 IP 段。

3. DKIM:selector 与空 p=

DKIM 记录挂在 <selector>._domainkey.<domain> 上,selector 由发信服务商给,常见形态是 google、20230601、s1。值里 p= 后面是公钥。

本机查到的一个特殊情况:

20230601._domainkey.gmail.com TXT: v=DKIM1; k=rsa; p=

p= 为空表示这把钥匙被吊销(按 DKIM 规范,空 p 即撤销)。这不是配置错误,是 Google 退役旧 selector 留下的记录。自己配 DKIM 时看到 p= 空,检查是不是复制时把公钥漏了。

实践要点:公钥用 2048 位;一条 selector 对应一把钥匙,换钥匙就换 selector,旧记录保留一段时间再删;TXT 值长,粘贴时不要手动加换行,Cloudflare 面板会自己处理分段。

4. DMARC:策略与报告

DMARC 记录只写在 _dmarc.<domain>,值是一串 key=value。两个真实例子:

_dmarc.google.com TXT: v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com
_dmarc.nuxt.dev  TXT: v=DMARC1;  p=none; rua=mailto:e9df...@dmarc-reports.cloudflare.net

p=reject 是最终形态,未通过对齐的邮件直接拒收;p=none 是观察模式,只收报告不动邮件。合理的推进顺序是 none 收到足够报告、确认所有合法发信源都在 SPF/DKIM 里、再升到 quarantine、最后 reject。直接上 reject 而漏配了某个发信服务,那部分业务邮件会静默消失。

rua 是聚合报告的接收地址,每天一封 XML。自己解析麻烦,Cloudflare 的 DMARC Management 会代收并汇总成面板数据,nuxt.dev 那条记录的 rua 就指向 dmarc-reports.cloudflare.net。这个功能免费,但要在面板里单独开启。

子域默认继承主域策略,需要单独控制时用 sp=;只想对部分邮件生效用 pct=,pct=10 表示 10% 的失败邮件按策略处理。

5. 在 Cloudflare DNS 里怎么填

记录名称类型内容
SPF@TXTv=spf1 include:_spf.example.net ~all
DKIMgoogle._domainkeyTXT服务商给的整段 v=DKIM1; ...
DMARC_dmarcTXTv=DMARC1; p=none; rua=mailto:...

三处容易错:名称栏不要填完整域名(面板会自动补根域,填全了会变成 _dmarc.example.com.example.com);值里不要自己加外层引号,面板保存时按需转义;DKIM 的公钥不要断行。这些写入动作本机未执行,没有该域的 Zone 权限。

6. 验证方法

Resolve-DnsName -Name example.com -Type TXT
Resolve-DnsName -Name _dmarc.example.com -Type TXT
Resolve-DnsName -Name google._domainkey.example.com -Type TXT

更直接的办法是给自己发一封信,在收件端看原始信头里的 Authentication-Results,三个结果(spf / dkim / dmarc)会逐项列出,比猜记录有效。信头里的 dkim=pass header.d= 还能告诉你到底是哪把 selector 在生效。

常见问题

三条记录缺一条行不行?

收信能收,但 DMARC 判定需要 SPF 或 DKIM 至少一项对齐通过。只配 SPF 时,转发场景几乎必然失败;只配 DKIM 时,退信与滥用检测会弱一些。三条齐配是成本最低的状态。

SPF 撞了 10 次查询上限怎么办?

先删掉不再使用的服务商 include,这是最常见的原因。删不动就做扁平化,把 include 展开成 IP 段直接写进记录,代价是对方换 IP 时你要跟着改,需要定期核查。

开了 p=reject 之后自己的邮件被退?

说明有合法发信源没被 SPF 覆盖,或者 DKIM 签名域与 From 域不对齐(常见于用第三方代发但没配自定义域签名)。先退回 p=none 收几天报告,按报告里的来源 IP 补齐再升回去。

小结

三条记录解决的是信任问题:SPF 划范围,DKIM 给凭证,DMARC 定规则并把违规情况反馈给你。它们不提升投递速度,只决定邮件被当成什么对待。

落地顺序建议反过来做:先开 p=none 收报告,看清真实发信源有哪些,再补 SPF 与 DKIM,最后收紧策略。这个顺序能让每一步都有数据支撑,而不是靠猜。域名层面的其他记录问题见 95-dns-troubleshooting-dig.md。

数据来源与检索时间

检索时间 2026-11-10。DNS 记录为本机 Resolve-DnsName 实查。

来源URL对应数据
Cloudflare 邮件相关 DNS 记录https://developers.cloudflare.com/dns/manage-dns-records/how-to/email-records/SPF、DKIM、DMARC 在 Cloudflare DNS 中的填写方式
Cloudflare DMARC Managementhttps://developers.cloudflare.com/dmarc-management/聚合报告代收、rua 指向 dmarc-reports.cloudflare.net
RFC 7208(SPF)https://www.rfc-editor.org/rfc/rfc720810 次 DNS 查询上限、redirect 与 include 语义
RFC 6376(DKIM)https://www.rfc-editor.org/rfc/rfc6376p= 为空表示密钥吊销
RFC 7489(DMARC)https://www.rfc-editor.org/rfc/rfc7489p / sp / pct / rua 语义与对齐规则

核验说明

  • 实查:gmail.com 的 TXT(v=spf1 redirect=_spf.google.com)、_spf.google.com 展开后的完整 SPF、_dmarc.google.com(p=reject)、20230601._domainkey.gmail.com(p= 为空)、qwik.dev 的 SPF、nuxt.dev 的 DMARC(p=none,rua 指向 Cloudflare 报表地址)。
  • 依据文档:10 次查询上限的计数规则、空 p= 的吊销语义、Cloudflare DMARC Management 的报告代收行为,分别依据 RFC 与官方文档,未做发信实测。
  • 未执行:在 DNS 面板写入或修改任何 SPF/DKIM/DMARC 记录(无 Zone 权限),也未发送测试邮件读取 Authentication-Results。文中记录值均来自公开查询,不是我配置的结果。