用 net/smtp 发邮件前必须配对的 3 个东西:smtp 服务器地址(如 "smtp.gmail.com:587")、有发信权限的邮箱账号及应用专用密码、符合 rfc 格式的邮件头与正文(含正确换行和编码)。

用 net/smtp 发邮件前必须配对的 3 个东西
Go 自带的 net/smtp 不是“调个函数就发”,它只管协议层,不处理认证、加密、正文组装这些事。你得自己凑齐:一个能连上的 SMTP 服务器地址(比如 "smtp.gmail.com:587")、一个有发信权限的邮箱账号+密码(或应用专用密码)、以及符合 RFC 格式的邮件头和正文(不是拼字符串那么简单)。
- Gmail 等主流服务现在基本禁用明文密码登录,必须用应用专用密码(在 Google 账户里开启两步验证后生成),直接填邮箱密码会报
535 5.7.8 Username and Password not accepted - 端口别硬记:
465对应 SSL(老式直连),587对应 TLS(STARTTLS 升级),Go 的smtp.SendMail只支持后者;若用465,得自己建tls.Conn再传给smtp.Client - 邮件正文必须带
Content-Type和\r\n\r\n分隔头与体,少一个换行就可能被当垃圾邮件或直接拒收
smtp.Auth 选哪个?smtp.PlainAuth 够用但有前提
smtp.PlainAuth 是最常用的选择,但它要求 SMTP 服务器明确声明支持 AUTH PLAIN(看 EHLO 响应里有没有这行)。Gmail、Outlook、腾讯企业邮都支持;但有些内网自建 Postfix 若没开 smtpd_sasl_auth_enable = yes,就会静默失败——连接成功,发信时卡住或返回 503 5.5.1 Error: authentication not enabled。
- 用户名字段填完整邮箱地址(如
"user@gmail.com"),不是昵称或前缀 - 密码字段填应用专用密码,不是账户密码;如果用的是 QQ 邮箱,注意它要求开启“SMTP服务”且密码是独立生成的授权码
- 域名参数(第一个参数)可以为空字符串,但不能为
nil;填错成服务器域名(如"smtp.qq.com")通常不影响,但某些严格实现会校验
构建合法邮件体:别手写 MIME,用 mime/multipart 或至少加好头
直接 fmt.Sprintf("To: %s\r\nSubject: %s\r\n\r\n%s", to, subject, body) 看似简单,实际埋雷:中文 subject 会乱码,附件无法附,HTML 正文不渲染,还容易触发反垃圾规则。最轻量的补救是手动加标准头,但必须严格:
From: <sender@example.com> To: <receiver@example.com> Subject: =?UTF-8?B?5byg5LiJ?= // Base64 编码的中文主题 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: base64 SGVsbG8gd29ybGQh // "Hello world!" 的 base64
- 所有头字段名首字母大写,冒号后空一格,结尾用
\r\n(不是\n) - 头与体之间必须是
\r\n\r\n,多一个或少一个字符都会让服务器解析失败 - 如果要发 HTML 邮件,
Content-Type改成text/html; charset=UTF-8,且确保 HTML 标签闭合、无 JS
超时和错误处理:别让 goroutine 卡死在 SendMail
smtp.SendMail 是阻塞调用,DNS 解析慢、网络抖动、服务器响应延迟都会导致它卡住几十秒。线上服务不设超时等于给自己埋定时炸弹。
立即学习“go语言免费学习笔记(深入)”;
- 用
context.WithTimeout包一层:传给smtp.SendMail的net.Conn必须是带 deadline 的,所以得自己 dial 并设SetDeadline,不能依赖SendMail内部逻辑 - 常见错误不要只打
err.Error():554 5.7.1 Message rejected due to content restrictions是内容被拦,421 4.7.0 Try again later是发太快被限频,得区分日志级别 - 别在循环里反复重试失败邮箱——Gmail 对单 IP 每分钟发信数有限制,连续错 3 次可能进临时黑名单
真正麻烦的从来不是“怎么发”,而是“发之前谁验证了邮箱格式、发之后谁确认对方收到了、失败时谁通知运维”。SMTP 本身没回调、没回执、没送达保证,这些得自己在业务层兜底。










