net/smtp不支持中文、HTML及附件的自动处理,易因MIME拼接错误、编码缺失或认证失败导致发送静默失败;必须显式构造auth、严格遵循RFC规范、使用multipart包处理附件与HTML。

别用 net/smtp 发带中文、HTML 或附件的邮件——它不处理 MIME、不自动编码、不校验头字段,90% 的“发送成功但收不到”或“主题乱码”都源于手动拼接出错。
smtp.SendMail 为什么返回 nil 错误却发不出去
最常见原因是 auth 参数为 nil 或空值:smtp.SendMail 不会报错,而是静默跳过认证,结果被 Gmail/163/QQ 直接拒收(530 5.7.0 Sender address rejected)。另一大坑是 From 地址和认证账号不一致,所有主流服务商都强制校验这个。
- 务必显式传入非 nil 的认证对象,例如
smtp.PlainAuth("", user, pass, host) -
user字符串必须和From头里的邮箱地址完全一致(包括大小写) - Gmail/Outlook 等已禁用明文密码,必须用「应用专用密码」,且需提前开启两步验证
- 测试时加一句
if auth == nil { log.Fatal("missing auth") },比查三天日志快得多
手动拼邮件内容时最容易漏掉的三处换行
smtp.SendMail 的 msg 参数不是正文,而是完整 RFC 5322 邮件字节流:头字段之间用 \r\n,头与正文之间必须是 \r\n\r\n(两个连续回车换行),少一个就可能被解析为纯正文、发件人丢失、进垃圾箱。
-
Subject含中文?必须用mime.WordEncoder编码,不能直接写"张三";否则 Outlook 显示=?UTF-8?B?5byg5LiJ?=或截断 -
From和To含中文昵称?用mime.FormatAddress("张三", "zhang@example.com")生成合规字符串 -
Content-Type: text/plain; charset=utf-8中的utf-8不能加引号,写成"utf-8"就是非法值
端口 587 和 465 的 TLS 处理方式完全不同
net/smtp 本身不封装 TLS 升级逻辑:587 是显式 STARTTLS(先明文连,再发 STARTTLS 命令),465 是隐式 TLS(一上来就得用 tls.Dial 连)。用错端口+加密组合,连接直接被 reset,错误提示常是 i/o timeout 或 connection refused,而非明确的 TLS 报错。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 选 587 端口:用
smtp.PlainAuth+smtp.SendMail即可,它内部会尝试 STARTTLS - 选 465 端口:必须自己
tls.Dial得到*tls.Conn,再传给smtp.NewClient,smtp.SendMail不支持 - 本地调试建议用
mailhog(docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog),避免触发公网 SMTP 限频
附件和 HTML 邮件必须用 multipart 构造
想发 HTML 正文+一个 PDF 附件?net/smtp 不提供任何封装,你得手写 MIME boundary、分段头部、base64 编码、结尾分隔符。错一个字符,收件方就显示为空白或乱码。
- 别自己拼
boundary="xyz",用mime/multipart包:w := multipart.NewWriter(wr),它自动生成唯一 boundary 并管理换行 - HTML 和纯文本共存?先
w.CreatePart()写multipart/alternative,再嵌套两个 part(text/plain和text/html) - 附件文件名含中文?
part.Header.Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(name)) - 调用
w.Close()必不可少,否则结尾 boundary 缺失,整个邮件结构损坏
真正难的不是写几行代码,而是每一封发出的邮件都要同时满足 RFC 5322、RFC 2045、服务商 SPF/DKIM 校验、客户端渲染兼容性——这些细节在 gomail.v2 里已被封装,而裸用 net/smtp 时,你得自己逐条对齐。


















