Vercel部署NextJs+NodeMailer时邮件无法送达问题排查
Next.js + Nodemailer 部署Vercel后无法发件的解决方案
问题背景
本地环境下用Next.js和Nodemailer实现的邮件发送功能完全正常,但部署到Vercel后,POST请求已触发(日志可查)、环境变量也已配置,邮件却无法送达。
核心排查与修复步骤
1. 修正环境变量命名
Vercel中NEXT_PUBLIC_前缀的变量会暴露给前端,敏感信息(如邮箱密码)不能用这个前缀,且后端Node.js环境读取非NEXT_PUBLIC_变量更稳定:
- 将环境变量
NEXT_PUBLIC_EMAIL改为EMAIL,NEXT_PUBLIC_EMAIL_PASSWORD改为EMAIL_PASSWORD - Vercel后台同步修改变量名称,重新部署项目
2. 修复Nodemailer配置错误
你的代码存在两处配置错误:
- auth参数格式错误:
auth.user和auth.pass应直接传字符串,不是对象(比如user: email而非user: { email }) - 混用配置模式:用Gmail时直接指定
service: "gmail"即可,无需手动写port和host
修正后的传输配置:
const transporter = nodemailer.createTransport({ service: "gmail", auth: { user: process.env.EMAIL, pass: process.env.EMAIL_PASSWORD, }, secure: true, })
3. 处理异步发送逻辑
当前代码中transporter.sendMail是异步操作,但你直接返回了响应,导致Vercel可能提前终止函数执行,邮件还没发送完成就被中断:
- 将API函数改为
async,用await等待邮件发送完成 - 错误时返回500状态码,让前端能捕获真实错误
修正后的contact.js:
export default async function (req, res) { let nodemailer = require('nodemailer') const transporter = nodemailer.createTransport({ service: "gmail", auth: { user: process.env.EMAIL, pass: process.env.EMAIL_PASSWORD, }, secure: true, }) const mailData = { from: `${req.body.name} <${req.body.email}>`, // 规范发件人格式:名称<邮箱> to: process.env.EMAIL, subject: `Message From ${req.body.name}`, text: `Message: ${req.body.message} | Name: ${req.body.name} | Phone: ${req.body.phone}`, html: `<p>Name: ${req.body.name}</p><p>Phone: ${req.body.phone}</p><p>Email: ${req.body.email}</p><div>${req.body.message}</div>` } try { await transporter.sendMail(mailData) res.status(200).json({ success: true }) } catch (err) { console.error(err) res.status(500).json({ success: false, error: err.message }) } }
4. 调整Gmail授权方式(若使用Gmail)
Vercel服务器IP属于陌生环境,Google会拦截常规密码登录:
- 开启两步验证后,创建应用专用密码,用该密码替换原邮箱密码
- 未开启两步验证的话,临时开启不太安全的应用访问(不推荐,建议用应用专用密码)
5. 优化Vercel函数超时(可选)
Vercel Serverless Function默认超时10秒,可通过vercel.json调整最大时长(上限10秒):
{ "functions": { "api/contact.js": { "maxDuration": 10 } } }
原始代码参考
API 请求函数
export const sendContactForm = async (data) => fetch("/api/contact", { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json", Accept: "application/json" }, }).then((res) => { if (!res.ok) throw new Error("Failed to send message") return res.json() })
Nodemailer 配置文件(Nodemailer.js)
import nodemailer from "nodemailer" const email = process.env.NEXT_PUBLIC_EMAIL const pass = process.env.NEXT_PUBLIC_EMAIL_PASSWORD export const transporter = nodemailer.createTransport({ service: "gmail", auth: { user: { email }, pass: { pass }, }, secure: true }) export const mailOptions = { from: { email }, to: { email }, }
前端提交逻辑
const onSubmit = async () => { setState((prev) => ({ ...prev, isLoading: true, })) try { await sendContactForm(values) setTouched({}) setState(initState) } catch (error) { setState((prev) => ({ ...prev, isLoading: false, error: error.message, })) } }
内容的提问来源于stack exchange,提问作者Igor Pilya
相关产品推荐
相关产品推荐

