Go用net/smtp连不上SMTP服务器主因是服务端拒绝,常见于未开启SMTP权限、未用应用专用密码或IP被限流;需启用SMTP服务、使用应用密码、选对端口(如Gmail用587、QQ邮箱用587/465),并避免硬编码25端口。

Go 用 net/smtp 发邮件为什么连不上 SMTP 服务器?
绝大多数连接失败不是代码写错,而是被 SMTP 服务端拒绝——常见于未开启 SMTP 权限、未使用应用专用密码、或 IP 被限流。Gmail、QQ 邮箱等主流服务商默认禁用“不安全应用访问”,必须手动开启并生成应用密码(非登录密码)。
实操建议:
- 确认邮箱已开启 SMTP 服务(如 QQ 邮箱:设置 → 账户 → POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV → 开启 SMTP 服务)
- 使用应用密码替代账户密码,尤其在启用两步验证后
smtp.Auth必须传入该密码 - 检查端口:Gmail 推荐
587(STARTTLS),QQ 邮箱用587或465(SSL),避免硬编码为25(多数云服务器已屏蔽) - 若用公司内网邮箱,注意 DNS 解析是否可达,可先用
telnet smtp.exmail.qq.com 587测试连通性
如何发送带附件和 HTML 正文的邮件?
Go 标准库不直接支持 multipart 邮件构造,需手动拼接 MIME 头与边界(boundary)。直接用 net/smtp 写太容易出错,推荐轻量第三方包 gopkg.in/gomail.v2(已维护稳定)或更现代的 github.com/go-mail/mail(v2+ 版本)。
关键点:
立即学习“go语言免费学习笔记(深入)”;
-
mail.NewMessage()创建消息体,.SetHeader("From", "...")和.SetAddressHeader("To", "...", "...")设置收发人 - HTML 正文用
.SetBody("text/html", ";纯文本备选用Hello
").AddAlternative("text/plain", "Hello") - 附件调用
.Attach("/path/to/file.pdf"),自动处理 MIME 类型与 base64 编码;若需自定义文件名,用.Attach("/path", mail.WithName("发票.pdf")) - 不要手动拼
Content-Type: multipart/mixed—— 库会根据AddAlternative和Attach自动推导结构
生产环境发信失败时怎么快速定位?
错误信息常被 smtp.SendMail 吞掉,只返回 error 接口,看不出是认证失败、DNS 解析超时,还是对方拒信。必须显式捕获底层错误类型。
调试建议:
- 用
errors.As(err, &net.OpError{})判断是否网络层失败(如 dial timeout) - 若错误含
"535 5.7.8"字样,基本是账号/密码错误或应用密码失效 - 若含
"554 5.7.1",多为发信方 IP 被拉黑,或收件人域名不存在 - 开启 SMTP 调试日志:用
gomail时传入gomail.SetDebugWriter(os.Stdout),能看到完整请求/响应原文 - 避免在循环中高频调用发信函数——QQ 邮箱单 IP 每小时上限约 200 封,超出直接封禁
要不要自己搭邮件中继服务?
除非有合规审计、私有域名白名单、或日均万级发信需求,否则不建议自建 Postfix / Sendmail。运维成本高,且大概率进垃圾箱——新 IP 缺乏反向 DNS、SPF/DKIM/DMARC 配置稍有偏差就会被拒收。
更务实的选择:
- 小流量(
- 中流量(1k–50k/天):接入 SendGrid、Mailgun 或国内的阿里云邮件推送(
dm.aliyuncs.com),它们提供 API、模板、送达率监控和退订管理 - 对接时注意:阿里云要求先在控制台申请发信域名并完成 DNS 验证,否则
SendEmail接口返回InvalidAccount.NotFound
真正难的从来不是“怎么发出去”,而是“怎么确保对方收得到、不进垃圾箱、能追踪打开率”。SMTP 连通只是第一步,后续的模板管理、退信解析、发送节流,都得单独设计。










