推荐使用 gomail.v2 库而非原生 net/smtp:它自动处理 mime、中文编码、html/附件、tls 配置及错误重试,三行代码即可安全发信,避免手动拼接邮件头、编码和连接导致的 4xx/5xx 错误。

Go语言发邮件,别硬刚 net/smtp 原生包——它不处理 MIME、不自动编码中文、不支持附件,一发 HTML 或带中文主题就 4xx/5xx 报错,90% 的“发送失败”都栽在这儿。
为什么 smtp.SendMail 经常发不出去?
这个函数只是底层 SMTP 协议的裸调用,连邮件头格式都要手动拼:"Subject: 中文主题\r\n" 不加 MIME-Version 和 Content-Transfer-Encoding,Gmail、Outlook 会直接当垃圾邮件或拒收;from 和 to 字段含中文昵称?不 base64 或 quoted-printable 编码就乱码;端口填错(比如该用 465 却填 587)+ TLS 模式不匹配,连接直接被 reset。
- 587 端口必须显式启用 STARTTLS(用
smtp.PlainAuth+smtp.SendMail时需自己 wrap TLS 连接,标准库没封装) - 465 端口是隐式 TLS,但
net/smtp默认不支持,得自己 dial tls.Conn 再传给smtp.SendMail - QQ 邮箱、163、Gmail 等已全面禁用明文密码登录,必须用「授权码」而非账户密码,且授权码要单独开启 SMTP 服务才生效
推荐用 gomail.v2:三行代码搞定 HTML / 附件 / 中文主题
它自动处理 RFC 2822 格式、MIME 分段、UTF-8 主题编码、HTML 正文识别,还内置重试和超时控制。比手写 strings.Builder 拼邮件体安全十倍。
package main
import "gopkg.in/gomail.v2"
func main() {
m := gomail.NewMessage()
m.SetHeader("From", "me@163.com")
m.SetHeader("To", "you@gmail.com")
m.SetHeader("Subject", "你好,这是一封 Go 发的邮件") // 自动编码
m.SetBody("text/html", "<h2>欢迎使用 <code>gomail</code>
</h2>")
d := gomail.NewDialer("smtp.163.com", 465, "me@163.com", "your-app-password")
d.TLSConfig = &tls.Config{InsecureSkipVerify: true} // 测试可开,生产请校验证书
if err := d.DialAndSend(m); err != nil {
panic(err) // 注意:真实项目里别 panic,要 log.Error
}
}
- HTML 邮件只需改
SetBody("text/html", ...),不用手动写MIME-Version头 - 添加附件用
m.Attach("/path/to/file.pdf"),自动分 multipart/mixed - 抄送/密送:用
m.SetHeader("Cc", "...")/m.SetHeader("Bcc", "...") - 如果用企业微信/钉钉网关等非标准 SMTP,记得设
d.SSL = false或d.StartTLSPolicy = gomail.MandatoryStartTLS
发信失败时,先查这三件事
别急着重写逻辑,95% 的问题出在配置层。
- 检查邮箱是否真正开启了 SMTP 服务(不是 POP3/IMAP)——例如 163 邮箱要在「设置 → POP3/SMTP/IMAP」里单独打开「SMTP服务」并记下授权码
- 确认端口和加密方式匹配:
465 → SSL/TLS,587 → STARTTLS,25 → 通常被 ISP 封禁,慎用
- 收件人地址是否被发件域拉黑?本地测试先发给自己,再用
telnet smtp.qq.com 587 看能否连通,排除防火墙或 DNS 问题
最麻烦的其实是错误静默——比如 gomail 的 DialAndSend 在认证失败时只返回 generic authentication failed,不会告诉你到底是用户名错、密码错还是授权码过期。真要 debug,得开 gomail.SetDebugWriter(os.Stdout) 看原始 SMTP 交互流。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











