SMTP连接失败多因服务端限制或网络策略,需先用telnet/openssl测端口连通性;Gmail等须用应用专用密码;国内云厂商常封25端口,优先选465/587并确认白名单;Flask-Mail需关闭MAIL_SUPPRESS_SEND且校验发件人;中文需用email.header.make_header和MIMEText指定UTF-8;并发发送应复用连接或接入专业邮件服务;务必配置SPF等DNS记录防拒收。

SMTP连接被拒绝或超时,smtplib.SMTPConnectError 怎么办
多数失败不是代码写错,而是 SMTP 服务端限制或本地网络策略卡住。比如公司内网屏蔽 587/465 端口,或 Gmail 账户未开启「应用专用密码」或「允许不够安全的应用」(已弃用,必须用应用密码)。
- 先用
telnet smtp.gmail.com 587或openssl s_client -connect smtp.gmail.com:465 -quiet测试端口连通性;不通就别折腾 Python 了 - Gmail / Outlook 等主流邮箱必须用应用专用密码(在账户安全设置里生成),不能直接填登录密码
- 国内服务器发信常被云厂商拦截(如阿里云封 25 端口),优先改用 465(SSL)或 587(STARTTLS),并确认服务商是否要求白名单域名或备案域名
-
smtplib.SMTP()默认不加密,连 587 必须紧接着调.starttls();连 465 应该用smtplib.SMTP_SSL(),混用会报SMTPServerDisconnected
Flask-Mail 发验证码时收件人收不到,但无报错
没报错 ≠ 邮件发出去了。Flask-Mail 默认异步关掉、错误静默,容易误判成功。真实场景中,邮件可能进垃圾箱、被 SPF/DKIM 拒绝,或 mail.send() 返回前就因超时被 WSGI 中断。
- 开发期务必加
app.config['MAIL_SUPPRESS_SEND'] = False(默认是True!),否则根本不会真发 - 检查
MAIL_DEFAULT_SENDER是否设为已验证的邮箱地址(如 Gmail 要求 sender == 登录账号) - 验证码邮件内容避免纯文本 + 链接,至少带简单 HTML 结构,否则 Hotmail/Outlook 更倾向归入垃圾邮件
- 用
msg.body和msg.html同时设内容,不要只设一个;Flask-Mail 不会自动 fallback - 发送后打印
msg.subject和msg.recipients,确认变量没被覆盖或为空列表
smtplib 手动发邮件,中文主题/正文乱码怎么解
SMTP 协议本身只认 ASCII,中文必须编码。常见错误是用 .encode('utf-8') 直接塞进 msg['Subject'],结果变成一堆 =?utf-8?b?...?= 但解码失败。
- 主题用
email.header.make_header():from email.header import make_headermsg['Subject'] = str(make_header([('验证码请求', 'utf-8')])) - 正文如果是纯文本,用
MIMEText(body, 'plain', 'utf-8');HTML 则用MIMEText(html, 'html', 'utf-8') - 千万别对整个
msg.as_string()再 encode —— 它内部已处理好 base64/quoted-printable 编码 - 测试时用 Gmail 收件,它对编码容错高;Apple Mail 或 Outlook 更敏感,乱码大概率是 header 编码方式不对
验证码邮件延迟高,或并发发多封时失败
SMTP 连接开销大,每次发都新建连接 + 认证,既慢又容易触发服务商频率限制(如 Gmail 限制 100 封/天,单 IP 短时高频会被临时封)。
立即学习“Python免费学习笔记(深入)”;
- 用连接池思路:实例化一次
smtplib.SMTP_SSL(),复用同一个对象发多封(注意线程安全,Flask 中建议 per-request 创建,或用threading.local) - Flask-Mail 默认不复用连接,可通过自定义
Mail类重写send()方法,在内部维持长连接 - 生产环境别裸用 SMTP 发验证码,应接入 SendGrid / Mailgun / 阿里云邮件推送,它们提供队列、重试、送达率统计
- 用户触发「重新发送」时,服务端必须校验间隔(如 60 秒),前端按钮也要禁用,否则一秒钟点五次,SMTP 服务器先扛不住
最常漏掉的是 DNS 设置:发信域名必须配 SPF 记录(如 v=spf1 include:_spf.google.com ~all),否则即使代码全对,Gmail 也直接拒收。这点查日志看不到,只能去收件方垃圾箱翻原始邮件头。










