resend
Resend 邮件服务与 DNS 配置指南
记录 Resend 发邮件的原理、DNS 配置及其交互过程。
目录
2. Resend 发邮件原理
3. SPF 记录
4. DKIM 记录
5. DMARC 记录
6. 与 MX 记录的区别
7. 配置示例
8. 常见问题
1. 核心概念:发邮件与 DNS 的关系
发邮件没有中心化限制——任何人都可以伪造发件地址,SMTP 协议本身不做验证。
验证发生在收件方。Gmail、Outlook 等收到邮件后,通过查询发件域名(tap.online)的 DNS 记录来判断邮件真伪,决定放入收件箱、垃圾箱或直接拒收。
所以域名拥有者需要在 DNS 中添加相应 TXT 记录来「授权」特定的发信服务。
2. Resend 发邮件原理
2.1 整体流程
你的代码 → Resend API → AWS SES → 收件人邮箱(如 Gmail)
↓
Gmail 查询域名 DNS
↓
SPF/DKIM/DMARC 验证
↓
收件箱 ✅ / 垃圾箱 ⚠️ / 拒收 ❌
2.2 Resend 底层使用 AWS SES
Resend 发邮件实际调用的是 Amazon SES 的邮件发送基础设施。所以在配置 SPF 时需 include:amazonses.com,因为发件 IP 来自 AWS。
3. SPF 记录
3.1 作用
声明哪些邮件服务器被授权代发你的域名邮件。
3.2 本项目的配置
类型: TXT
名称: @ (或 tap.online)
值: v=spf1 include:amazonses.com ~all
3.3 语法说明
| 部分 | 含义 |
|------|------|
| v=spf1 | 版本标识 |
| include:amazonses.com | 允许 Amazon SES 的 IP 段发信 |
| ~all | 其他来源软失败(标记可疑但通常不直接拒收) |
| -all | 其他来源硬失败(直接拒收) |
3.4 验证过程
收件服务器查询 tap.online 的 TXT 记录
↓
获取 SPF: "v=spf1 include:amazonses.com ~all"
↓
邮件来源 IP 属于 amazonses.com
↓
SPF 验证通过 ✅
4. DKIM 记录
4.1 作用
使用公钥加密验证邮件完整性——确认邮件在传输中未被篡改,且确实由授权服务发出。
4.2 本项目的配置
类型: TXT
名称: resend._domainkey.tap.online
值: v=DKIM1; p=公钥字符串...
4.3 语法说明
| 部分 | 含义 |
|------|------|
| resend | 选择器(selector),由 Resend 指定,标识使用哪把公钥 |
| _domainkey | DKIM 标准前缀 |
| v=DKIM1 | DKIM 版本 |
| p=... | 公钥内容(Base64 编码) |
4.4 验证过程
发件时 Resend 用私钥给邮件签名,邮件头携带选择器名 "resend"
↓
收件服务器查询 resend._domainkey.tap.online 的 TXT 记录
↓
获取 DKIM 公钥: "v=DKIM1; p=MFww..."
↓
用公钥解密签名 → 匹配 → 邮件未被篡改 ✅
5. DMARC 记录
5.1 作用
告诉收件方:当 SPF 和/或 DKIM 验证失败时,该怎么处理这封邮件。同时可以接收验证报告,监控冒用情况。
5.2 策略选项
| 策略 | 值 | 行为 |
|------|----|------|
| 监控 | p=none | 不处理,仅收报告(推荐测试期使用) |
| 隔离 | p=quarantine | 标记为垃圾邮件 |
| 拒收 | p=reject | 直接拒收,不进收件箱 |
5.3 配置示例
类型: TXT
名称: _dmarc.tap.online
值: v=DMARC1; p=quarantine; rua=mailto:report@tap.online
| 参数 | 含义 |
|------|------|
| v=DMARC1 | 版本标识 |
| p=quarantine | SPF/DKIM 不通过时放入垃圾箱 |
| rua=mailto:... | 接收聚合报告的邮箱地址 |
6. 与 MX 记录的区别
| 记录 | 用途 | 方向 | 本项目是否需要 |
|------|------|------|--------------|
| MX | 指定收件服务器 | 收信方向 | ❌ 无需收件 |
| SPF (TXT) | 声明谁可以代发 | 发信方向验证 | ✅ 必须 |
| DKIM (TXT) | 用公钥验证签名 | 发信方向验证 | ✅ 必须 |
| DMARC (TXT) | 验证失败时的处理策略 | 发信方向策略 | ✅ 推荐 |
本项目只通过 Resend 发信(验证码),不需要配置 MX 记录。
7. 配置示例
7.1 Cloudflare DNS 配置
| 类型 | 名称 | 值 | 说明 |
|------|------|-----|------|
| TXT | @ | v=spf1 include:amazonses.com ~all | SPF 授权 AWS SES |
| TXT | resend._domainkey | v=DKIM1; p=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL4Dk... | DKIM 公钥 |
| TXT | _dmarc | v=DMARC1; p=quarantine; rua=mailto:admin@ta.online | DMARC 策略 |
7.2 本地测试(本项目特性)
在 ENVIRONMENT=development 模式下,/api/auth/send-code 会将验证码一并返回在响应中,方便开发调试。
8. 常见问题
8.1 不加这些 DNS 记录能发信吗?
能发,但收件方无法验证邮件真实性,大概率会:
- 无 SPF → 来源不明,可能判为垃圾邮件
- 无 DKIM → 无法验证完整性,可能被标记为伪造
- 信誉评分降低 → Gmail/Outlook 放入垃圾箱甚至退信
8.2 DMARC 可以省略吗?
可以。SPF + DKIM 已经能保证基本通过。但 DMARC 能控制验证失败时的行为并提供监控报告,推荐生产环境配置。
8.3 多个发信服务怎么办?
SPF 支持多个 include:
v=spf1 include:amazonses.com include:spf.sendgrid.net ~all
DKIM 不同服务使用不同的选择器,互不冲突。
8.4 验证顺序总结
收件方收到发往 user@tap.online 的邮件
↓
① SPF 验证: 发件 IP 是否在 SPF 记录的授权列表中?
↓
② DKIM 验证: 用域名 DNS 中的公钥解密签名,内容是否一致?
↓
③ DMARC: 根据上述结果,按策略处理(放行/隔离/拒收)
↓
最终决定 ✅/⚠️/❌
DNS 记录类型参考
A 记录(Address Record)
将域名指向一个 IPv4 地址。
app.tap.online A 123.123.123.123
用户访问 app.tap.online → DNS 返回 123.123.123.123 → 浏览器直连该 IP。
适用场景:服务器有固定公网 IP(VPS、裸金属)。
CNAME 记录(Canonical Name Record)
将域名别名到另一个域名,由目标域名解析 IP。
app.tap.online CNAME your-app.vercel.app
用户访问 app.tap.online → DNS 返回 your-app.vercel.app → 再查 your-app.vercel.app 的 A 记录拿到 IP。
适用场景:部署在云平台(Vercel、Netlify、Cloudflare Pages),IP 不固定或共享。
注意:CNAME 不能与同名的其他记录共存。
MX 记录(Mail Exchange Record)
指定接收该域名邮件的邮件服务器地址,包含优先级(数字越小越优先)。
tap.online MX 10 mail.tap.online
tap.online MX 20 backup-mail.tap.online
当别人发邮件到 user@tap.online 时,发件服务器查到 MX 记录 → 优先尝试优先级 10 的 mail.tap.online,不可达则用优先级 20 的备用服务器 → 再通过 A 记录解析 IP 投递。
适用场景:需要接收邮件时配置(Gmail Workspace、Namecheap Email Forwarding 等)。
快速对比
| 记录 | 指向 | 作用 | 配 IP? | 本项目中需要? |
|------|------|------|---------|-------------|
| A | IPv4 地址 | 告诉浏览器「服务器在哪」 | 固定 IP | 如果用固定服务器则需 |
| CNAME | 另一个域名 | 给域名起别名,指向云平台 | 不需要 | 如果用 Cloudflare Pages 则需 |
| MX | 邮件服务器域名 | 告诉发件方「信投递到哪」 | 不需要(再查 A) | ❌ 无需收件 |
注意:同一个域名不能同时配置 A 和 CNAME,DNS 规范不允许。
DNS 递归解析与授权链
为什么在 tap.online 里配 trip.com 的记录没用?
DNS 是一个分层授权的系统,解析器只问「管这个域名的人」:
用户查询 trip.com
↓
① 根服务器 → 告诉我 .com 的权威 DNS 是谁?
② .com 服务器 → trip.com 的权威 DNS 是 ns1.trip.com(由 trip.com 的注册商管理)
③ 解析器 → 去 ns1.trip.com 查询 trip.com 的 A 记录
↓
✅ 拿到结果
全过程从未经过 Cloudflare/Namecheap 的 tap.online 解析器 ✋
解析器真能直接找到正确的权威 DNS 吗?
能。完整的递归解析路径:
递归解析器(如 8.8.8.8)
↓
根域名服务器(全球 13 组,硬编码在系统中)
→ "去查 .com 的服务器"
↓
.com TLD 服务器(Verisign 管理)
→ "trip.com 的权威 DNS 是 ns1.trip.com"
↓
trip.com 的权威 DNS(trip.com 注册商设置)
→ "trip.com 的 A 记录是 xxx.xxx.xxx.xxx"
↓
返回给用户 ✅
你在自己面板里加一条,会怎么样?
tap.online 的 DNS 区域里写:
trip.com A 1.2.3.4
这条记录确实存在于 Cloudflare 的数据库里,但没有任何递归解析器会来查它。解析器通过根 → .com → trip.com 的授权链直接找到了 trip.com 自己的权威 DNS,根本不会路过你的 Cloudflare。
类比:你在自己家门上贴了张纸条写着「隔壁老王的地址是 xxx」,但快递员永远会直接去敲老王家的门问地址,不会看你家的纸条。
关键概念:授权(Delegation)
tap.online → 你的 Cloudflare/Namecheap 权威
trip.com → trip.com 注册商/托管商的权威
两者是平行的,互不隶属,互相看不到对方区域的记录。
- 你可以写
trip.com A 1.2.3.4到tap.online的区域——你的 DNS 管理面板不阻止你 - 但递归解析器不会看到它,因为 DNS 协议只查权威 DNS,而
trip.com的权威 DNS 不是你 - 所以这条记录加了等于没加,完全无效
例外情况:子域名授权(NS 记录)
如果 tap.online 有子域名 xxx.tap.online,你可以用 NS 记录把子域名的解析权委派给其他 DNS 服务器。但这是委派子域名,不是跨域名声明。