Node.js 项目里要发一封邮件,听起来是最不值一提的需求,真做起来却容易踩一堆坑:TLS 该怎么起、465 和 587 有什么区别、HTML 和纯文本要不要自己拼 multipart、附件怎么编码、OAuth2 令牌过期谁管。Nodemailer 把这一整层收进了单个包里,官网那句「easy as cake」不算吹牛——它是 Node.js 生态里用得最多的邮件发送库,而且零运行时依赖。

Nodemailer 官方 Logo:蓝绿折线信封图形与 Nodemailer 字样

Nodemailer 官方 Logo(白底处理,无水印、无叠加广告)

一、先看它凭什么站住脚

官方文档列的理由挺实在,我按自己的理解重排一下:

  • 零运行时依赖:所有东西都在一个包里,不用装原生扩展、不用系统库,丢进容器或云函数都不会冒出编译问题。
  • 安全优先:设计上刻意避开曾在其他 Node 邮件库出现过的远程代码执行类漏洞;TLS / STARTTLS 加密连接自动处理。
  • Unicode 完整支持:正文里放中文、emoji、任意字符都不用额外编码。
  • 跨平台一致:Linux、macOS、Windows 行为相同,没有原生 addon。
  • 内容能力强:同时给 html 和 text 就自动构造 multipart;附件、内嵌图片、DKIM 签名、OAuth2 认证、插件 API、代理支持都在。

授权是 MIT-0(MIT No Attribution),比常规 MIT 更宽松:随便用,连署名都不强制。作者是 Andris Reinman,Logo 由 Sven Kristjansen 设计。

二、发一封信只要三步

核心抽象只有一个词:transporter。它负责连邮件服务、认证、投递,建一次就该全程复用——每发一封都新建会反复开连接、反复认证,纯属浪费。

const nodemailer = require("nodemailer");

// 1. 建 transporter:应用启动时建一次,全局复用
const transporter = nodemailer.createTransport({
  host: "smtp.example.com",
  port: 587,
  secure: false, // 587 用 STARTTLS;只有 465 才设 true
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

// 可选:上线前先验一次连通性
await transporter.verify();

// 2 + 3. 组装消息并发送
const info = await transporter.sendMail({
  from: '"示例团队" <[email protected]>',
  to: "[email protected], [email protected]",
  subject: "你好",
  text: "纯文本兜底内容",
  html: "<b>HTML 正文</b>",
});

console.log("已投递:", info.messageId);

createTransport(transport[, defaults]) 的第一个参数可以是配置对象、连接 URL(如 smtp://user:pass@host:587),也可以是自己实现的传输插件;第二个参数给所有消息合并默认值,统一 from 或自定义头很方便。sendMail() 不传回调就返回 Promise。

返回的 info 里有 messageId、envelope、accepted / rejected、response、ehlo、envelopeTime / messageTime / messageSize 等。有个细节值得记:多收件人时只要有一个被服务器接受就算发送成功,其余失败的地址躺在 rejected 里,得自己检查。错误按 err.code 分类处理:ECONNECTION/ETIMEDOUT 稍后重试、EAUTH 查凭据、EENVELOPE 查地址。

Nodemailer 发送链路示意图:应用代码 → 可复用 Transporter → 传输方式 → 邮件服务器,下方列出零依赖、HTML+纯文本、附件、TLS、OAuth2/DKIM、Unicode/插件六项能力

发送链路与开箱能力一览(本地绘制,无第三方素材)

三、传输方式不止 SMTP

传输适用场合
SMTP默认方案,配置项最全,支持连接池、OAuth2、DKIM、代理
Amazon SES已经在 AWS 上、想走 SES API 而不是 SMTP 端点
Sendmail把消息交给本机的 sendmail 程序,容器里要留意它需要子进程
Stream / JSON只生成 RFC 822 消息流或 JSON,不真发,测试与调试神器
自定义插件实现 send(mail, callback) 接口就能接任意投递通道

本地开发还可以配 Ethereal 测试账号,邮件进一次性收件箱而不打扰真人,nodemailer.getTestMessageUrl(info) 能直接拿预览链接。

四、版本与运行环境:升级前先看这段

  • Nodemailer 10 要求 Node.js 20+;老项目继续留在 9.x 线。ESM 与 CommonJS 双构建都提供,import 和 require 都能用。
  • 类型定义已内置:10 之后用 TypeScript 重写并自带声明,务必移除 @types/nodemailer,否则声明冲突。常用类型从包根导出;nodemailer/lib/* 下的类要用默认导入。
  • 其他运行时:Deno 2 可用;ESM 构建跑在 Bun 上;Cloudflare Workers 需开 nodejs_compat,且只有基于 SMTP 的传输适用——sendmail 要子进程、运行时不允许关闭证书校验,所以在那儿写 tls.rejectUnauthorized: false 会直接报错。

五、新版一个很实用的能力:取消单次发送

10 之后可以在消息里塞一个 AbortSignal,单独取消某一次 sendMail():

await transporter.sendMail({
  from: "[email protected]",
  to: "[email protected]",
  text: "Hello",
  signal: AbortSignal.timeout(30_000),
});

SMTP 与池化 SMTP 支持它:预先中止就不进入处理,中途中止会停掉后续插件、移除排队消息或关掉该消息占用的连接,池里其他投递不受影响;Promise 与回调两种写法都拿到名为 AbortError、code: 'EABORT' 的错误。传进来的值不是 AbortSignal 会以 ECONFIG 失败。

但有两条边界别踩:取消只停本地处理,不能证明服务器没已接收这封信,别据此自动重试;stream、JSON、sendmail、SES 一旦消息交出去就忽略 signal,仍会正常发送。自定义传输要显式声明 supportsAbortSignal: true 并自己同步注册监听、清理资源。

六、常见故障速查

官方 FAQ 那几条基本能覆盖八成工单,我按排查顺序整理:

  1. 加载报 SyntaxError:多半是 Node 版本低于 20,升 Node 或退回 9.x。
  2. ETIMEDOUT:先查服务器和本机的防火墙,邮件端口常被 ISP 主动封禁。
  3. 一台机器能发、另一台不行:不是防火墙就是 SMTP 服务端对来源做了认证限制。
  4. TLS 报错:杀毒软件做 MITM 时 Node 不认它的证书;新 Node 只接受 TLS 1.2+;secure 只有 465 才设 true,设 false 不等于不加密,服务端支持时仍会 STARTTLS 升级;老 Node 对最新 Let's Encrypt 证书链支持不全,优先升级 Node 而不是关校验。
  5. 改了 hosts 没生效:Node 用 c-ares 解析域名,不走系统 DNS 配置;必要时把 IP 直接写进 host,同时用 tls.servername 补上域名,否则证书校验过不了。
  6. Gmail 死活不通:官方的态度很直白——Gmail 要么很顺、要么完全不行,与其硬修不如换服务商;如果卡在 OAuth2,那是另一码事。

七、几句实话

系统通知、账单、验证码、告警、带附件的报表这类「业务触发型」邮件,Nodemailer 几乎是 Node 侧的默认答案:一个包、几行配置、不引入额外服务,配好 verify() 和错误码分支就能稳稳跑。它的定位是把消息可靠地交给邮件服务器,不是替你运营投递。

所以别指望它做队列重试、退信解析、送达率统计、退订管理、限速与信誉维护——这些要么自己在应用层补(队列 + 幂等键 + 失败重试),要么交给专门的投递服务。生产环境三条建议:凭据一律走环境变量、别写进代码;transporter 全局复用,量大时开连接池;对取消掉的发送别盲目自动重试,先确认服务端状态。把边界划清楚,这个库能用得很省心。

一句话总结:Nodemailer 把「Node.js 发邮件」这件事做成了不需要再纠结的基础设施;真正要想的,是邮件发出去之后的那一段。

本文整理自公开资料并做重写,原始出处:https://github.com/nodemailer/nodemailer。文中配图已去除站点叠加水印与推广信息;版权归原作者所有。

Last modification:November 10, 2026
如果觉得我的文章对你有用,请随意赞赏