代码写好、本地也能发出去,部署到阿里云 ECS 后却超时、被拒收,或者直接进垃圾箱。验证码、订单提醒、系统告警一旦发不出去,用户注册和转化都会受影响。

这篇文章按排查顺序,讲阿里云服务器发送邮件失败的几类常见原因,给出对应的处理办法,最后附一份可以直接改用的 Python 配置示例。
一、先分清是网络问题还是信誉问题
邮件发不出去,表现看起来差不多,原因却分两类。一类是网络层连不上对方的邮件服务器,连接阶段就超时。另一类是连接成功了,但邮件被对方拒收或分到垃圾箱,这属于发件信誉的问题。两类的排查方向完全不同,先判断属于哪一类,能省下很多时间。
网络层的卡点通常在端口。阿里云默认对 ECS 的 25 端口出站有限制,具体策略以控制台说明为准。如果代码直接连接 smtp.qq.com:25 或 smtp.163.com:25,连接大多会卡住,最后报 connection timed out。这种情况下代码往往没有错,换账号、改密码也解决不了。
二、三个常见案例
案例1:25 端口超时
某小型电商团队把订单通知邮件部署在阿里云 ECS 上,本地测试一切正常。上线后用户下单却收不到确认邮件,日志里反复出现连接超时。排查发现他们用的是默认的 25 端口。改成 465(SSL)或 587(STARTTLS)之后,邮件恢复正常发送。
案例2:域名没认证,发送量突增
一个 SaaS 团队用阿里云服务器批量发营销和通知邮件。前几天一切顺利,之后大量邮件突然进了垃圾箱。他们没有配置 SPF、DKIM 和 DMARC 记录,发件域名和服务器 IP 之间的对应关系也不清楚,加上短时间内发送量猛增,被邮件服务商判定为可疑。补全 DNS 记录、把发送频率降下来之后,送达情况有了改善。
案例3:用个人邮箱密码发信
有开发者习惯把个人邮箱的密码写进配置文件,让服务器直接用它发信。这样做风险大。异常登录可能导致邮箱被冻结,明文密码留在服务器上也容易泄露。更稳妥的做法是为应用单独创建发信凭据,走专门的发信服务。
三、三种发信方式怎么选
方案一:阿里云邮件推送(DirectMail)
DirectMail 是阿里云官方的发信服务,针对云环境做了优化,多数情况下是最省心的选择。上手时有三件事要做:在控制台添加并验证发信域名,按提示配置 SPF 和 DKIM 记录;为应用创建专用的 SMTP 账号;发送量较大时,设置发送速率限制,并定期处理退信。
方案二:第三方 SMTP 服务
如果团队已经在用 SendGrid、Mailgun 或 Amazon SES,直接使用它们的 SMTP 接口发信即可。这类服务一般会提供送达率和退信报告,适合有多渠道发信需求的团队。选择节点时留意国内网络的访问质量。跨境链路会增加延迟,验证码这类时效性强的邮件最容易感受到。
方案三:本地中继或公司邮件网关
也可以在服务器上部署邮件中继,或者把邮件转发给公司内部的邮件网关,由网关统一对外发出。这种方式需要额外维护中继服务,适合对发信有较强管控要求的企业环境。个人项目或小团队通常不必走这条路。
四、可直接参考的配置示例
下面是 Python 标准库 smtplib 的写法,通过 465 端口建立 SSL 连接。代码里的账号、密码和地址都是占位符,替换成自己的配置即可。敏感信息从环境变量读取,不要写进代码,也不要提交到仓库。
import os
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
SMTP_HOST = os.environ.get("SMTP_HOST", "smtpdm.aliyun.com")
SMTP_PORT = int(os.environ.get("SMTP_PORT", "465"))
SMTP_USER = os.environ["SMTP_USER"]
SMTP_PASSWORD = os.environ["SMTP_PASSWORD"]
SENDER_NAME = "系统通知"
SENDER_EMAIL = os.environ["SENDER_EMAIL"]
def send_mail(to_addr: str, subject: str, body: str) -> None:
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = subject
msg["From"] = formataddr((SENDER_NAME, SENDER_EMAIL))
msg["To"] = to_addr
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=10) as server:
server.login(SMTP_USER, SMTP_PASSWORD)
server.sendmail(SENDER_EMAIL, [to_addr], msg.as_string())
if __name__ == "__main__":
send_mail("test@example.com", "测试邮件", "这是一封来自阿里云服务器的测试邮件。")
这段代码有三处设计值得注意。SMTP_SSL 直接建立加密连接,绕开了 25 端口。凭据从环境变量读取。设置了 10 秒超时,连接卡住时进程不会一直阻塞。上线后如果仍然超时,先在服务器上测试 465 端口能否连通,例如执行 telnet smtpdm.aliyun.com 465,通了再去查安全组和防火墙规则。
五、上线前的检查清单
- 确认代码连接的是 465 或 587 端口,而不是默认的 25 端口。
- 检查 ECS 的安全组是否放行出站流量。安全组没问题时,再看系统防火墙。
- 在 DNS 中补全发信域名的 SPF 和 DKIM 记录,添加后发一封测试邮件,确认认证通过。
- 使用专用的发信账号,不要把个人邮箱密码写进配置文件。
- 发送量突然增加时,先放慢节奏并分批发送,避免一次性集中投递。
- 定期查看退信和投诉数据,把无效地址移出发送列表,不要反复重发。
排查阿里云服务器发信失败,建议按顺序来:先用 telnet 确认端口能通,再核对域名认证,最后看发送节奏。网络不通时改 DNS 不会有任何效果,顺序不能颠倒。