You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地Zoho Mail+Nodemailer正常,生产服务器邮件发送失败问题

React + Node.js 部署后 Nodemailer 无法发送邮件问题

本地 localhost 环境下,React + Node.js 项目使用 Nodemailer 发送邮件功能完全正常,但部署到托管服务器后无法发送邮件。前端请求返回响应如下:

Response {type: 'basic', url: 'https://example.com/register', redirected: false, status: 200, ok: true, …}

但邮件实际并未发出,以下是相关代码及问题排查方案:


相关代码

package.json

"proxy": {
  "/auth/google": {
    "target": "http://localhost:8000/"
  }
},

ContactForm.jsx

import React, { useState, useEffect } from 'react'

const ContactForm = () => {
    const [fname, setFname] = useState("");
    const [lname, setLname] = useState("");
    const [email, setEmail] = useState("");
    const [subject, setSubject] = useState("");
    const [msg, setMsg] = useState("");
    const [message, setMessage] = useState('');

    useEffect(() => {
        setTimeout(() => setMessage(''), 9000);
    }, [message])

    const sendEmail = async (e) => {
        e.preventDefault();

        // 先发送请求再清空表单,避免参数为空
        setMessage(`Thank you ${fname} ${lname} for enquiry. We will back to you asap.`);
        
        const res = await fetch("/register", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ fname, lname, email, subject, msg })
        });

        console.log(res);

        setFname("");
        setLname("");
        setEmail("");
        setSubject("");
        setMsg("");
    }

    return (
        <>
            <div className="contact-form">
                <div className="contact-form-info">
                    <div className="contact-title">
                        <h3>TELL US YOUR PROJECT</h3>
                    </div>
                    <form id="contact-form">
                        <div className="contact-page-form">
                            <div className="contact-input">
                                <div className="contact-inner">
                                    <input type="text" placeholder="First Name *" id="first-name" value={fname} onChange={(e) => setFname(e.target.value)} />
                                </div>
                                <div className="contact-inner">
                                    <input type="text" placeholder="Last Name *" id="last-name" value={lname} onChange={(e) => setLname(e.target.value)} />
                                </div>
                                <div className="contact-inner">
                                    <input type="text" placeholder="Email *" id="email" value={email} onChange={(e) => setEmail(e.target.value)} />
                                </div>
                                <div className="contact-inner">
                                    <input type="text" placeholder="Subject *" id="subject" value={subject} onChange={(e) => setSubject(e.target.value)} />
                                </div>
                                <div className="contact-inner contact-message">
                                    <textarea placeholder="Message *" value={msg} onChange={(e) => setMsg(e.target.value)}></textarea>
                                </div>
                            </div>
                            <div className="contact-submit-btn">
                                <button className="submit-btn" type="submit" onClick={sendEmail} >Send Email</button>
                                <p className="form-messege pt-3">{message}</p>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </>
    )
}

export default ContactForm;

app.js

const express = require("express");
const app = express();
const router = require("./routes/router");
const cors = require("cors");

// 部署时使用服务器分配的动态端口,本地默认8000
const port = process.env.PORT || 8000;
app.use(express.json());
app.use(cors());
app.use(router);

app.listen(port, () => {
    console.log(`Server start on port no : ${port}`)
})

router.js

const express = require("express");
const router = new express.Router();
const nodemailer = require("nodemailer");

router.post("/register", (req, res) => {
    const { fname, lname, email, subject, msg } = req.body;

    // 校验必填参数
    if (!fname || !lname || !email || !subject || !msg) {
        return res.status(400).json({ status: 400, error: "Missing required fields" });
    }

    const transporter = nodemailer.createTransport({
        host: "smtppro.zoho.in",
        port: 465,
        secure: true,
        auth: {
            // 用环境变量存储敏感信息,避免硬编码
            user: process.env.EMAIL_USER || "info@mydomain.com",
            pass: process.env.EMAIL_PASS || "myAppPassword",
        },
        // 开启调试模式,便于排查SMTP连接问题
        debug: true,
        logger: true
    });

    const mailOptions = {
        from: "info@mydomain.com",
        cc: "info@mydomain.com",
        to: email,
        subject: "Website Form",
        html: `
            First Name : ${fname}<br>
            Last Name : ${lname}<br>
            Email : ${email}<br>
            Subject : ${subject}<br>
            Message : ${msg}
        `
    };

    transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
            console.error("Email send error:", error);
            return res.status(500).json({ status: 500, error: error.message });
        }
        console.log("Email sent:", info.response);
        res.status(201).json({ status: 201, info });
    });
});

module.exports = router;

问题排查与解决方案

1. 前端参数为空问题

原代码中先清空表单再发送请求,导致后端收到的参数全为空。调整顺序为先发送请求,再清空表单,确保参数正确传递。

2. 代理配置失效

package.json 仅配置了 /auth/google 的代理,前端请求的 /register 路径未被代理覆盖。部署环境下:

  • 要么将前后端部署在同一域名下,或配置后端 CORS 允许前端域名访问
  • 要么前端使用完整的后端 API 地址(通过环境变量配置,如 process.env.REACT_APP_API_URL)

3. 端口硬编码问题

托管服务器通常分配动态端口,需改用 process.env.PORT 获取端口,本地默认 fallback 到 8000。

4. 敏感信息泄露风险

直接硬编码邮箱账号密码存在安全隐患,部署时通过服务器环境变量存储这些信息,后端通过 process.env 读取。

5. SMTP 端口限制

部分托管服务商限制出站 SMTP 连接,可尝试:

  • 改用 587 端口,设置 secure: false 并开启 requireTLS: true
  • 确认服务商是否允许使用外部 SMTP 服务,部分服务商要求使用自有邮件服务

6. 调试日志缺失

开启 Nodemailer 的 debug: true 和 logger: true,查看服务器端日志,确认 SMTP 连接是否成功、具体错误信息。

7. 错误处理不完善

原代码的 try/catch 无法捕获回调形式的异步错误,需在 sendMail 的回调中正确捕获并返回错误,同时打印详细日志便于排查。


内容的提问来源于stack exchange,提问作者Shahnawaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 00:29:53