Next.js+Nodemailer发送事务邮件遇队列/未送达问题求助
我在Next.js网站里用Nodemailer搭配SMTP凭证发送订阅、重置密码这类事务邮件,先后试了Mailgun、Sendinblue、MSG91、Postmark的免费版,要么邮件进积压队列,要么SMTP服务器返回队列状态;用Sendinblue API也没用,邮件根本到不了收件人邮箱。
以下是我的API代码(跳过注释逻辑,查看Nodemailer部分)和请求返回结果:
API代码
import ConnectDB from "@/utils/connect_db" import User from "@/models/user" const nodemailer = require('nodemailer'); const jwt = require("jsonwebtoken") const forgotPassword = async (req, res) => { try { if (req.method === 'POST') { await ConnectDB() // let user = await User.findOne({ email: req.body.email }) // if (!user) user = await User.findOne({ username: req.body.email }) //because user can put the username or email in the same field and api should verify from both ways // if (!user) return res.status(404).json({ success: false, msg: "You don't have an account with this email!" }) // const token = jwt.sign({ id: user._id }, process.env.SECRET_KEY, { expiresIn: '2m' }) // USING Postmark const transport = nodemailer.createTransport({ host: "smtp.postmarkapp.com", port: 587, auth: { user: "34dd6ee8-8015-444a-92e5-25194cf742cc", pass: "34dd6ee8-8015-444a-92e5-25194cf742cc" } }); // create email message object const message = { from: 'binarshadsaad6@gmail.com', to: 'saad19rsf@gmail.com', text: "hellow this is a text email body to check the email service", subject: 'You subscribed our Newsletter', html: `<html><body><h1>Fitte Moooo!</h1><p>Dear valuable Jheengu this email is to notify you that you have successfully recieved the 'Fitte Moo' reward for literally just existing at this time of morning.</p></body></html>` }; // send email using nodemailer let info = await transport.sendMail(message); console.log(info) res.json({ success: true, info }) } else { res.status(400).json({ success: false, msg: "bad request, you are using wrong request method!" }) } } catch (error) { console.log(error) res.status(500).json({ success: false, msg: "Internal server error occurred, pleae try again later" }) } } export default forgotPassword
请求返回结果
{ "success": true, "info": { "accepted": [ "saad19rsf@gmail.com" ], "rejected": [], "ehlo": [ "PIPELINING", "SIZE 20480000", "VRFY", "ETRN", "AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5", "AUTH=PLAIN LOGIN CRAM-MD5 DIGEST-MD5", "ENHANCEDSTATUSCODES", "8BITMIME", "DSN" ], "envelopeTime": 622, "messageTime": 452, "messageSize": 858, "response": "250 2.0.0 Ok: queued as 3B28D414C40", "envelope": { "from": "binarshadsaad6@gmail.com", "to": [ "saad19rsf@gmail.com" ] }, "messageId": "<50a33941-a951-b4eb-2290-781a58eda83d@gmail.com>" } }
解决思路
1. 验证发件人地址合法性
从返回结果看,Postmark已接收邮件并进入队列,但未送达的核心原因是发件人地址未通过服务商验证。你用的binarshadsaad6@gmail.com属于第三方邮箱,未在Postmark后台完成授权/域名验证,会被收件方反垃圾系统拦截。
解决:在Postmark后台验证自定义域名,使用该域名下的邮箱作为发件人(如noreply@yourdomain.com);若坚持用Gmail,需在Postmark后台完成该邮箱的授权流程。
2. 优化邮件内容合规性
测试邮件里的俚语(如Fitte Moooo!、Jheengu)、不符功能的主题(当前是重置密码接口,主题却写订阅通知)会触发反垃圾标记。事务邮件需:
- 主题清晰匹配功能(如「请重置你的账户密码」)
- 内容正式简洁,避免无意义俚语
- HTML结构规范,添加必要的合规元素(如退订链接,部分服务商强制要求)
3. 修复SMTP配置细节
Postmark的587端口用STARTTLS加密,需补充配置确保连接安全:
const transport = nodemailer.createTransport({ host: "smtp.postmarkapp.com", port: 587, secure: false, // 587端口无需直接SSL,用STARTTLS requireTLS: true, // 强制加密连接 auth: { user: "你的Postmark Server Token", pass: "你的Postmark Server Token" } });
4. 排查投递日志
不要只依赖Nodemailer返回,去Postmark后台查看投递日志,里面会明确显示邮件是被拒收、进垃圾箱还是延迟投递。若收件方是Gmail,可先检查垃圾箱,或把发件人添加到联系人列表。
5. 改用服务商官方SDK
SMTP层可能存在兼容问题,直接用Postmark官方SDK更稳定:
// 先安装依赖:npm install postmark import postmark from 'postmark'; const client = new postmark.ServerClient('你的Postmark Server Token'); const sendResult = await client.sendEmail({ From: 'noreply@yourdomain.com', // 已验证的发件地址 To: 'saad19rsf@gmail.com', Subject: '账户密码重置', TextBody: '点击链接重置密码:xxx', HtmlBody: '<p>点击链接重置密码:<a href="xxx">重置</a></p>' });
6. 检查免费版限制
所有服务商免费版有发送量限制(如Postmark每月1000封),新注册账户可能需要人工审核才能正常投递,若长期队列积压,可联系服务商客服确认账户状态。
内容的提问来源于stack exchange,提问作者Dark Reaper

