Nodemailer本地正常但Cyclic.sh部署后无法发送邮件问题排查
以下是发送邮件的Node.js函数:
function sendEmail(num, email, customerName) { var readHTMLFile = function (path, callback) { fs.readFile(path, { encoding: 'utf-8' }, function (err, html) { if (err) { throw err; callback(err); } else { callback(null, html); } }); }; let transporter = nodemailer.createTransport({ host: "smtp.gmail.com", secure: true, port: 465, auth: { user:process.env.USEREMAIL, pass:process.env.USERPASS }, }); readHTMLFile(__dirname + '/views/layouts/first.html', function (err, html) { var template = handlebars.compile(html); var replacements = { otp: `${num}`, customerName: `${customerName}` }; var htmlToSend = template(replacements); var mailOptions = { from: process.env.USEREMAIL, to: email, subject: "Hello ✔", html: htmlToSend }; transporter.sendMail(mailOptions, function (error, response) { if (error) { console.log(error); } else { console.log("Email sent"); } }); }); }
该函数在本地localhost:8000运行时一切正常,但部署到Cyclic.sh平台后,应用正常运行且返回“Email sent”的成功提示,却实际未收到邮件,请问该函数存在什么问题?
1. Gmail SMTP 授权限制是核心问题
本地能正常发邮件,大概率是因为你本地环境被Gmail信任(比如开启了低安全应用访问,或者用了普通密码),但Cyclic.sh属于云平台,Gmail默认会拦截这类非信任环境的登录请求。解决办法:
- 给你的Gmail账号开启两步验证,然后生成一个应用专用密码,把这个密码替换环境变量
USERPASS的值,别再用原来的账号密码。 - 这是云环境用Gmail SMTP的标配操作,普通密码在云服务器上基本都会被拦截。
2. 文件路径与错误处理的潜在问题
__dirname + '/views/layouts/first.html'在本地没问题,但Cyclic.sh部署后,项目的文件结构可能和本地有差异,或者__dirname的指向不符合预期,导致模板文件读取失败。而且你的readHTMLFile函数错误处理有bug:如果fs.readFile出错,先throw err再调用callback(err),但throw会直接终止回调函数,callback根本不会执行,这会导致后续的邮件发送逻辑完全没触发。建议:
- 用
path.join(__dirname, 'views', 'layouts', 'first.html')拼接路径,避免不同系统下的路径分隔符问题。 - 去掉
throw err,直接调用callback(err),防止异步回调里的未捕获异常。
3. 邮件被拦截或归为垃圾邮件
就算发送请求成功,Cyclic.sh的服务器IP可能被邮箱服务商标记为垃圾IP,导致邮件直接进垃圾箱或者被拦截。可以:
- 先检查收件人的垃圾邮件文件夹,看看是不是被归类了。
- 确保
mailOptions里的from地址和你的Gmail账号完全一致,避免因为发件人不符被拦截。
4. 错误日志没抓全
当前代码里transporter.sendMail的回调只打印错误,但Cyclic.sh的日志可能没被你实时查看,实际发送过程中可能有错误,但你只看到了“Email sent”的提示。建议把错误信息用console.error(error)输出,这样能在Cyclic.sh的日志面板里看到具体的错误原因,比如授权失败、IP被限制等,方便定位问题。
内容的提问来源于stack exchange,提问作者kumari

