根本原因是现代邮箱禁用明文密码登录,须用应用专用密码或开启smtp权限;gmail需先开两步验证,qq邮箱需手动开启smtp并获取授权码;端口与协议须匹配(587-starttls/465-ssl),用户名为完整邮箱,密码为授权码而非登录密码。

Go 用 net/smtp 发邮件为什么总卡在认证失败?
根本原因不是密码错了,而是多数现代邮箱(如 Gmail、Outlook、QQ 邮箱)已禁用「明文密码登录」,必须用「应用专用密码」或开启 SMTP 服务权限。Gmail 还强制要求开启两步验证后才能生成应用密码;QQ 邮箱需在「账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务」里手动开启 SMTP 并获取授权码。
实操建议:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
smtp.SendMail第一个参数是地址+端口,别写成"smtp.qq.com:587"就完事——要确认端口对应协议:587对应 STARTTLS,465对应 SSL/TLS,协议不匹配会静默失败 - 用户名必须是完整邮箱地址(如
"user@qq.com"),不能只写前缀"user" - 密码字段填的是「授权码」或「应用专用密码」,不是登录密码;Gmail 的应用密码是 16 位小写字母+数字组合,复制时注意别带空格
- 如果用企业微信/钉钉等内网 SMTP 网关,得确认它是否支持 PLAIN 认证(
smtp.PlainAuth),有些只认 LOGIN 或 CRAM-MD5
如何构造带附件和 HTML 正文的 multipart 邮件?
Go 标准库没有开箱即用的 MIME 构建器,net/smtp 只负责传输,正文和附件得手动拼 multipart/mixed 和 multipart/alternative 结构。直接手写 boundary 和头字段极易出错,比如换行符用 \n 而非 \r\n,或 HTML 部分没套 text/html Content-Type,导致 Outlook 显示源码。
实操建议:
- 优先用
gopkg.in/gomail.v2(维护活跃)或github.com/go-gomail/gomail(v3 分支),它们自动处理编码、boundary、Content-ID 和附件 MIME 头 - HTML 正文必须显式设置
SetBody("text/html", ...),纯文本部分用SetBody("text/plain", ...),否则某些客户端只显示纯文本 - 附件路径用
Attach("/path/to/file.pdf")即可,库会自动读取并设置Content-Disposition: attachment;若需 inline 图片,得用Embed()+cid:xxx引用 - 中文附件名要用
mime.BEncoding.Encode("UTF-8", "报告.pdf")编码,否则 Outlook 解析为乱码
发信慢、超时或被当垃圾邮件?检查这三点
不是网络问题,大概率是发信行为触发了反垃圾策略:单 IP 每分钟发信数超限、HELO 域名不存在、From 地址未验证、无 SPF/DKIM 记录。国内云服务器(如阿里云、腾讯云)25 端口默认封禁,必须改用 465/587。
实操建议:
- 用
time.AfterFunc包裹smtp.SendMail加超时控制,避免协程卡死;典型值设30 * time.Second,比默认的 30 秒略长一点防抖动 - HELO/EHLO 域名必须是真实可解析的域名(如
"mail.example.com"),不能填"localhost"或 IP;本地开发可用"dev.local",但上线必须切真实域名 - From 地址必须与 SMTP 登录账号一致,且该邮箱需在发信域有 SPF 记录(如
v=spf1 include:qq.com ~all);没域名就用免费邮箱中转,别硬扛自建 SMTP - 批量发送务必加
time.Sleep(1 * time.Second)间隔,QQ 邮箱限制 100 封/小时,Gmail 是 500 封/天
生产环境要不要自己搭 SMTP 服务?
不要。自建 Postfix + Dovecot 维护成本远高于预期:证书续期、黑名单监控、退信解析、TLS 版本升级、DKIM 密钥轮转……中小项目直接用 SendGrid、Mailgun 或腾讯云 SES,它们提供 API Key、Webhook 回调、发送统计,且 IP 信誉已预热。
实操建议:
- Go 调用 SendGrid 用官方
sendgrid-goSDK,sg.Send(&msg)一行发信,附件、模板、变量替换全内置 - 若必须走 SMTP 协议(如对接内部系统),选腾讯云 SES,它兼容标准 SMTP 接口,但后台能看到每封信的投递状态和拒信原因
- 所有密码、API Key 必须从环境变量读取(
os.Getenv("SMTP_PASSWORD")),绝不能硬编码或塞进配置文件提交到 Git - 日志里禁止打印完整邮件内容或收件人列表,最多记
to: xxx@***.com, status: ok
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










