见过一种做法:查到一个 SPF 生成器,把记录贴进 DNS,再把 DMARC 直接写成 p=reject,觉得这样最严格、最安全。第二天财务说报价单被对方系统退回了——给这个域名发信的不止网站,还有企业邮箱、ERP、开票平台,它们都不在 SPF 里,DMARC 又要求严格拒绝。
认证可以分步做,每一步都能单独验证、单独回退。下面按「先不影响现有邮件」的顺序走一遍。
动 DNS 之前,先把发信来源摸清楚。少列一个,后面收紧策略时就会挡掉它。
来源 | 走哪个服务 | 你能否改它的 DNS | 发信量 | 备注 |
|---|---|---|---|---|
网站表单通知 | 云主机 / 邮件推送 | 是 | 每天几十封 | 本文重点 |
企业邮箱 | 邮箱服务商 | 是 | 每天几十封 | 通常已配好 |
财务 / 开票系统 | 第三方 SaaS | 不一定 | 每周几封 | 最容易漏 |
群发 / 开发信 | 推送平台 | 是 | 按活动 | 单独子域名更稳 |
监控告警 | 自建脚本 | 不定 | 不定 | 常被遗忘 |
清单列完,再决定哪些来源是必须保住的。这一步做完,后面每改一条记录,你都知道自己在影响谁。
如果这台主机和站点是从别人手上接过来的,配置历史无从查起,可以先跑一遍 AumCreate 的站点检查工具:https://aumcreate.com/tools/site-check?utm_source=tencent&utm_medium=content&utm_campaign=tencent-wp-mail-dmarc-progressive 。它替不了你做认证,但能省掉「这条记录到底是谁加的」这种考古。
SPF 是放在域名 TXT 里的一条 v=spf1 ... 记录,作用是告诉收件方「这些服务器有权用我的域名发信」。两条并存会直接 permerror,等于没配。
dig +short example.com TXT | grep spf1如果查出两条以上,先合并成一条再往下走。合并时盯的是查询预算,不是记录条数:
查询次数上限 10 次。 include: 每出现一次算一次查询,被 include 的那条 SPF 里如果还有 include,继续累加。超过 10 次,整条 SPF 失效。数的方法是逐条 dig 下去看返回里还有没有 include,手工记。
void lookup 上限 2 次。 查询一个不存在的域名算 void lookup,超过 2 次同样判失败。写错的 include 域名比不写更糟。
单条 TXT 的字符串长度上限 255 字符。 超了要拆成多段字符串,多数 DNS 服务商的输入框会自动处理,自己拼的时候留意。
结尾用 ~all 还是 -all:~all 是软失败,标成可疑但多数情况仍会收;-all 是硬失败,不在列表里的直接拒。现有系统不确定的时候先留 ~all,等 DMARC 观察期的报告证明没有漏网来源,再改 -all。
DKIM 是签名:发信方用私钥给邮件签名,公钥挂在 DNS 里,收件方拿公钥验。它不影响现有什么能发、什么不能发,所以放在第一步风险最低。
自建 Postfix 的话,OpenDKIM 的配置大致是这样。关键是 Mode sv 和 milter_default_action = accept——只签名、只验证,不做拒绝,milter 进程挂掉也不会把邮件拦下来。
# /etc/opendkim.conf
Domain example.com
Selector mail
KeyFile /etc/opendkim/keys/example.com/mail.private
SigningTable refile:/etc/opendkim/signing.table
KeyTable refile:/etc/opendkim/key.table
InternalHosts /etc/opendkim/trusted.hosts
Mode sv# /etc/opendkim/signing.table
*@example.com mail._domainkey.example.com
# /etc/opendkim/key.table
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
# /etc/opendkim/trusted.hosts
127.0.0.1
::1
localhost# /etc/postfix/main.cf
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept公钥发布到 mail._domainkey.example.com,值从控制台或 opendkim-genkey 生成,以你手上的公钥为准:
dig +short mail._domainkey.example.com TXT验证方式是给自己发一封,看邮件头里的 Authentication-Results 有没有 dkim=pass。这一步通过之前,不要动 SPF。
改 SPF 之前先想清楚:网站邮件到底是「主机直接发出去」还是「提交给邮件推送服务代发」。
腾讯云 CVM 默认对出站 25 端口有限制,需要单独申请解封;弹性公网 IP 的反向解析(PTR)也要提交工单,而且一个 IP 只对应一个 PTR。自建直发意味着端口、PTR、IP 信誉都得自己扛,新 IP 还得慢慢预热。对绝大多数站点来说,走 587 提交给邮件推送服务更省事,认证和信誉由服务方承担。
走代发的话,SPF 里要加的是服务方给的 include,不是主机 IP。同时注意对齐:DMARC 检查的是 From 头里的域名和 SPF/DKIM 的域名是否一致。代发场景下 Return-Path 往往是服务方的域,SPF 就算 pass,也可能因为域名不一致而不算对齐——这正是先配 DKIM 的原因,DKIM 签名用的是你自己的域,天生对齐。
DMARC 记录长这样,先只观察:
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; fo=1; adkim=r; aspf=r"p=none:只发报告,不影响投递。这是「先不影响现有邮件」的核心。rua:聚合报告收件地址,每天一封,内容是 zip 包,里面是 XML。adkim / aspf 用 r(宽松)起步,别一上来就 s(严格)。报告不要攒着不看。解压后按来源 IP 汇总,看 pass 比例、哪些 IP 在 fail、fail 的那些 IP 是不是清单里的已知系统。有人用脚本解析 XML,也可以直接搜在线解析工具,把 zip 传上去看。
观察两到四周,确认没有意外来源之后,按这个节奏收紧,每一步之间留几天:
v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com
v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@example.com
v=DMARC1; p=reject; rua=mailto:dmarc@example.compct 是百分比,从 25 开始意味着只有四分之一的邮件按这个策略处理,出问题也只影响一部分。报告里出现你没见过的来源,先联系对方确认,别急着加进 SPF。
回退很简单:把记录改回上一条。前提是动之前把 TTL 调到几分钟,不然改了也要等缓存过期才生效。
子域名默认继承主域策略,如果子域名有独立的发信系统还没配好,可以单独写 sp=none,别让它跟着主域一起被拒。
认证配好之后,站内的发信方式也要跟着改。用一个 mu-plugin 接管 PHPMailer,不用装插件:
<?php
// wp-content/mu-plugins/smtp-relay.php
add_action( 'phpmailer_init', function ( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->Host = getenv( 'SMTP_HOST' ) ?: 'smtp.example.com';
$phpmailer->Port = 587;
$phpmailer->SMTPAuth = true;
$phpmailer->Username = getenv( 'SMTP_USER' ) ?: '';
$phpmailer->Password = getenv( 'SMTP_PASSWORD' ) ?: '';
$phpmailer->SMTPSecure = 'tls';
$phpmailer->From = getenv( 'SMTP_FROM' ) ?: 'no-reply@example.com';
$phpmailer->FromName = 'Example';
$phpmailer->Sender = $phpmailer->From;
} );密码只从环境变量读,不要写进代码,也不要提交到版本库。注入方式跟着你的运行环境走:php-fpm 在池配置里给进程加环境变量,systemd 用 Environment=,容器用 secret;值本身放进密钥管理系统,文件里只留 SMTP_PASSWORD=<你的密钥> 这种占位说明。
# 在启动 PHP 的环境里注入,不要出现在仓库里
export SMTP_PASSWORD="$(your-secret-tool read smtp/password)"Sender 影响的是信封发件人(Return-Path),From 影响的是收件人看到的发件人。两者域名一致,对齐才稳。有些推送服务会强制改写 Return-Path,以服务商文档为准。
发一封测试信,看完整头和认证结果:
swaks --to you@example.com --from no-reply@example.com \
--server smtp.example.com:587 --tls \
--auth LOGIN --auth-user "$SMTP_USER" --auth-password "$SMTP_PASSWORD"先看头,别猜。Authentication-Results 里 SPF、DKIM、DMARC 三个都 pass 且对齐,才轮到查别的。
顺序是:认证结果 → 域名和 IP 信誉(查黑名单)→ 退信率和投诉率 → 内容(链接数量、纯文本替代、图片占比)→ 链路(Received 跳数、投递延迟)→ 互动信号(打开、回复、被标垃圾)。
前两项是硬门槛,后几项是打分项。很多人卡在「认证都过了为什么还是进垃圾箱」,八成是域名太新、发信量突然放大,或者名单里躺着一堆早就失效的地址。新域名和新 IP 要慢慢加量,第一天发五十封、第二天发一百封,比一次发五千封安全。
平台负责的是基础设施:IP 池、认证、退信处理、速率控制。它不能保证进收件箱——收件方的过滤规则、用户投诉、名单质量都在平台之外。
写进合同的应该是可核查的东西:退信率、投诉率的处理阈值,出现黑名单时谁负责申诉,退款条件。别接受「保证送达率 99%」这种说法,没有哪家能对别人的收件箱做承诺。
dig 查 SPF,确认只有一条,include 数量在 10 以内。dig 查 mail._domainkey,公钥能取到。p=none 起步,rua 地址能收到报告。pct 和 p。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。