根本原因是dns解析失败或网络策略拦截,go的net/smtp默认依赖系统dns且不支持自定义resolver,导致smtp.dial因域名无法解析而超时或报错。

为什么 net/smtp 发邮件总卡住或报 dial tcp: lookup smtp.example.com: no such host
根本原因不是代码写错了,而是 DNS 解析失败或网络策略拦截。Go 的 net/smtp 默认走系统 DNS,不支持自定义 resolver;如果目标 SMTP 域名(比如 smtp.gmail.com)在当前环境无法解析,smtp.Dial 就会直接超时或报这个错误。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先用
nslookup smtp.gmail.com或dig smtp.gmail.com确认 DNS 可通 - 若在容器或内网环境,检查
/etc/resolv.conf是否被覆盖,或尝试换用 IP 直连(如142.250.189.106:587),但要注意 TLS 证书校验可能失败 - 加超时控制:别用裸
smtp.Dial,改用net.DialTimeout+smtp.NewClient,避免阻塞整个 goroutine
auth.SMTPAuth 类型选错导致 535 5.7.8 Username and Password not accepted
常见误区是看到 Gmail / Outlook 要账号密码,就默认用 auth.PlainAuth,但现代 SMTP 服务大多要求 STARTTLS 后再认证,且部分服务商(如 Gmail)已禁用“低安全性应用”,必须用 App Password 或 OAuth2。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- Gmail 必须开两步验证,生成 App Password 替代账户密码;用
auth.PlainAuth时username填邮箱,password填 16 位 App Password - 别硬编码密码——从环境变量读:
os.Getenv("SMTP_PASSWORD") - 若用企业邮箱(如腾讯企业邮),确认是否强制要求
auth.CRAMMD5Auth;不匹配时服务器会静默拒绝,日志只显示认证失败
构造邮件正文时 text/plain 和 text/html 混排出乱码或被当垃圾邮件
net/smtp 本身不处理 MIME,得自己拼。常见错误是手动拼 \r\n 不规范、边界符重复、Content-Type 缺少 charset 声明,导致中文变问号,或 Gmail 直接折叠成「显示原始消息」。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
mime/multipart包组装 multipart/alternative,别手写分隔线 - 每个 part 显式声明
Content-Type: text/plain; charset=utf-8,HTML 部分同理 - HTML 正文里避免内联 JS/CSS,图片用 cid 引用而非外链——否则 Outlook 会屏蔽、Gmail 当钓鱼
发信后没报错但收件箱收不到,排查方向在哪
标准库不会告诉你「邮件进了垃圾箱」或「被 SPF/DKIM 拒绝」,它只管 TCP 层投递成功。真正拦住邮件的,是接收方 MTA 的反垃圾策略。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 先查发信域名的 DNS 记录:
dig TXT yourdomain.com看有没有 SPF、DKIM、DMARC;缺失任意一项,Gmail/Outlook 极大概率拒收 - 用
smtp.SendMail时传入真实发件人邮箱(不是随便写的from@example.com),否则 SPF 校验失败 - 测试阶段用
mail-tester.com打分,重点看「SPF record found」和「DKIM signature valid」是否勾选
最麻烦的点往往不在 Go 代码里,而在 DNS 配置和邮箱服务商的策略细节上——比如腾讯企业邮要求 MAIL FROM 和 From 头完全一致,差一个大小写都会退信。










