大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
SES 邮件
这一课你将能做到
- 配置 SES 验证域名和邮箱地址,申请从沙盒(Sandbox)模式移出
- 用 Spring Boot 集成 SES 发送注册验证码、课程通知等事务性邮件
- 配置 DKIM、SPF、DMARC 提升邮件送达率,并监控发件人声誉
为什么要学
邮件是 SaaS 产品的「生命线」——注册验证、密码重置、订单通知、课程提醒都依赖它。本站如果迁到 AWS,SES 是成本最低、集成最顺的邮件服务。面试中「怎么保证邮件不进垃圾箱」「DKIM 是什么」「邮件发送失败怎么处理」是高频问题。
人话:SES 是 AWS 的「邮局」。你不需要自己搭 SMTP 服务器,不用跟各大邮箱服务商(Gmail、QQ 邮箱、网易)打交道,SES 帮你搞定信誉和送达。代价是:新账号有沙盒限制,每天只能发 200 封,只能发给已验证的邮箱。
先建立图像
SES 邮件发送流程:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 本站后端 │────→│ SES │────→│ 收件人 │ │ 反馈循环 │
│ (Spring Boot)│ │ (SMTP/API) │ │ (Gmail/QQ) │────→│ (Bounce/ │
│ │ │ │ │ │ │ Complaint) │
└─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘
│
┌──────┴──────┐
│ SNS 通知 │
│ + 声誉监控 │
└─────────────┘
邮件认证协议:
| 协议 | 作用 | 配置位置 | 本站建议 |
|---|---|---|---|
| SPF | 声明哪些服务器有权代发 | DNS TXT 记录 | 必须配 |
| DKIM | 邮件签名,防篡改 | SES 自动生成,DNS CNAME | 必须配 |
| DMARC | 告诉收件方怎么处理伪造邮件 | DNS TXT 记录 | 建议配 |
| BIMI | 在邮件客户端显示品牌 Logo | DNS TXT 记录 | 可选 |
跟着做
步骤 1:验证域名(推荐)或邮箱地址
# 验证域名(推荐,可发任意该域名邮箱)
aws ses verify-domain-identity --domain codenav.io
# 获取验证记录(CNAME 类型,加到 DNS)
aws ses get-identity-verification-attributes --identities codenav.io
# 验证单个邮箱(沙盒模式测试用)
aws ses verify-email-identity --email-address admin@codenav.io
验证域名后,SES 会返回 3 条 CNAME 记录,需要添加到 Route53:
# 假设 SES 返回了 _amazonses.codenav.io 的 CNAME
aws route53 change-resource-record-sets --hosted-zone-id ZXXXXXXXXXX --change-batch file://ses-dns.json
步骤 2:配置 DKIM
# 启用 DKIM
aws ses set-identity-dkim-enabled --identity codenav.io --dkim-enabled
# 获取 DKIM 记录
aws ses get-identity-dkim-attributes --identities codenav.io
SES 会返回 3 条 CNAME 记录(xxx._domainkey.codenav.io),添加到 DNS 后,Gmail 等客户端会显示「已通过验证」。
步骤 3:配置 SPF 和 DMARC
SPF 记录(Route53 TXT 记录):
"v=spf1 include:amazonses.com ~all"
DMARC 记录(_dmarc.codenav.io TXT 记录):
"v=DMARC1; p=quarantine; rua=mailto:postmaster@codenav.io; pct=100"
步骤 4:申请移出沙盒模式
沙盒限制:
- 每天最多发 200 封
- 每秒最多发 1 封
- 只能发给已验证的邮箱
移出步骤:
- 打开 SES 控制台 → Account Dashboard → Request production access
- 填写:用例类型(Transactional)、网站 URL、预计发送量、退信处理流程
- 等待审核(通常 24 小时内)
步骤 5:Spring Boot 集成 SES
添加依赖:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sesv2</artifactId>
</dependency>
SesService.java:
@Service
public class SesService {
private final SesV2Client sesClient;
public SesService() {
this.sesClient = SesV2Client.builder()
.region(Region.AP_SOUTHEAST_1)
.build();
}
public void sendVerificationEmail(String to, String code) {
Destination destination = Destination.builder()
.toAddresses(to)
.build();
Content subject = Content.builder()
.data("【CodeNav】注册验证码")
.build();
Body body = Body.builder()
.html(Content.builder()
.data("<h2>验证码:" + code + "</h2><p>5 分钟内有效,请勿泄露。</p>")
.build())
.build();
Message message = Message.builder()
.subject(subject)
.body(body)
.build();
SendEmailRequest request = SendEmailRequest.builder()
.fromEmailAddress("noreply@codenav.io")
.destination(destination)
.content(EmailContent.builder().simple(message).build())
.build();
sesClient.sendEmail(request);
}
}
步骤 6:配置退信和投诉通知
# 创建 SNS Topic 接收退信通知
aws sns create-topic --name ses-bounce-notifications
# 配置 SES 发送退信到 SNS
aws ses set-identity-notification-topic --identity codenav.io --notification-type Bounce --sns-topic arn:aws:sns:ap-southeast-1:123456789012:ses-bounce-notifications
aws ses set-identity-notification-topic --identity codenav.io --notification-type Complaint --sns-topic arn:aws:sns:ap-southeast-1:123456789012:ses-bounce-notifications
退信处理 Lambda:
import json
import boto3
ses = boto3.client('sesv2')
dynamodb = boto3.resource('dynamodb')
def lambda_handler(event, context):
for record in event['Records']:
message = json.loads(record['Sns']['Message'])
if message['notificationType'] == 'Bounce':
bounced_emails = message['bounce']['bouncedRecipients']
for recipient in bounced_emails:
email = recipient['emailAddress']
# 加入黑名单,不再发送
add_to_suppression_list(email)
# 更新用户状态
mark_user_email_invalid(email)
讲清楚要点
- 沙盒模式是 SES 的新手村:所有新账号默认在沙盒,限制发送量和收件人。必须申请 production access 才能正常发邮件。
- 发件人声誉(Sender Reputation):SES 监控你的退信率(Bounce Rate)和投诉率(Complaint Rate)。退信率 > 5% 或投诉率 > 0.1% 会被警告,持续恶化会被暂停发送权限。
- DKIM 签名:SES 自动生成 2048 位 RSA 密钥对,私钥签名邮件,公钥放 DNS。收件方验证签名,确认邮件没被篡改。
- 配置集(Configuration Set):SES 的高级功能,用来跟踪发送事件(发送、送达、打开、点击、退信),可以按业务线分组统计。
- 专用 IP(Dedicated IP):默认用 SES 共享 IP,声誉受其他用户影响。大发送量(>10万封/天)建议买专用 IP($24.95/月/IP),并做 IP 预热(逐步增加发送量)。
你可能会懵的地方
-
SES 和 SendGrid、Mailgun 怎么选? SES 最便宜($0.10/1000 封),与 AWS 集成最好。SendGrid 功能更全(邮件模板、A/B 测试、营销自动化),但贵。Mailgun 开发者体验好,API 简洁。本站初期用 SES,需要营销自动化再考虑 SendGrid。
-
为什么邮件进了垃圾箱? 常见原因:没配 DKIM/SPF/DMARC;发件人声誉差;邮件内容像垃圾邮件(太多链接、大写、感叹号);收件人没把你加白名单。用 mail-tester.com 测评分。
-
SES 能收邮件吗? 能,SES 有 Inbound 功能,可以把收到的邮件存 S3 或触发 Lambda。适合做「回复邮件自动创建工单」这类场景。
-
事务性邮件和营销邮件要分开吗? 要。事务性邮件(验证码、订单确认)用独立子域名(如
mail.codenav.io),营销邮件(推广、 Newsletter)用另一个(如news.codenav.io)。这样营销邮件的投诉不会影响事务性邮件的送达。 -
SES 的速率限制是多少? 沙盒:1 封/秒,200 封/天。生产:默认 14 封/秒,5 万封/天,可申请提额。超限时返回
Throttling错误,需要指数退避重试。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 不处理退信继续发 | 退信率飙升,被 SES 暂停 | 监听 SNS 退信通知,自动加入黑名单 |
用 admin@codenav.io 发验证码 | 显式发件人地址不专业 | 用 noreply@codenav.io,配好 MX 记录 |
| 邮件内容全是图片 | 被当垃圾邮件 | 图文混排,加纯文本版本 |
| 不做 IP 预热就大量发送 | 新 IP 声誉低,进垃圾箱 | 专用 IP 前 2-4 周逐步增加发送量 |
| 把 SES SMTP 密码硬编码 | 泄露风险 | 用 IAM Role 或 Secrets Manager |
卡点急救
- 发送报
MessageRejected: Email address is not verified:还在沙盒模式,收件人邮箱没验证。验证收件人邮箱,或申请移出沙盒。 - Gmail 显示「未经验证」:DKIM 没配好。检查 DNS CNAME 记录是否生效(用 dig 或 nslookup),等 72 小时 DNS 传播。
- 发送速率被限(Throttling):降低发送频率,加指数退避重试。如果业务需要更高并发,提工单申请提额。
面试常问
-
怎么保证邮件不进垃圾箱? 技术层面:配 DKIM + SPF + DMARC,用专用 IP 并预热,保持低退信率/投诉率。内容层面:避免垃圾邮件关键词、图文比例合理、提供退订链接。运营层面:只发给主动订阅的用户,定期清理无效邮箱。
-
DKIM 的原理是什么? 发件方用私钥对邮件头和正文生成签名,放在
DKIM-Signature头。收件方从 DNS 查询公钥,验证签名。如果邮件被篡改,签名验证失败。 -
SES 退信率多少算安全? AWS 建议退信率 < 5%,投诉率 < 0.1%。超过会收到警告,持续恶化会被暂停发送权限(Account Under Review)。重要业务建议控制在 2% 以内。
自测三问
- SES 沙盒模式有哪些限制?怎么移出?
- DKIM、SPF、DMARC 分别解决什么问题?
- 如果 SES 账户因为高退信率被暂停,恢复流程是什么?
小结一句话
SES 是 AWS 的邮件服务,沙盒模式先验证再发,DKIM/SPF/DMARC 保送达,退信投诉要监控,声誉坏了全玩完。
延伸
- AWS SES 官方文档
- SES 送达最佳实践
- 至此,AWS 核心 12 课全部完成。建议下一步:用 CloudFormation/Terraform 把整套架构代码化,或准备 AWS SAA 认证考试。