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

Netlify部署Express后端与React前端的CORS问题求助

问题

使用React前端调用Netlify托管的Node.js Express后端时遭遇CORS问题,浏览器报错:

'Access to XMLHttpRequest at 'https://<my_api_domain>/api/forgotpassword' from origin 'localhost:8888' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.'

关键现象:

  • 本地开发环境完全正常,仅部署到Netlify后出现问题
  • 仅调用forgotpassword接口且执行数据库查询/写入或bcrypt加密操作时触发CORS错误,无这些耗时操作时不会出现
  • 前端与后端独立部署,域名不同

相关代码

api.js主文件

'use strict';
const express = require('express');
const serverless = require('serverless-http');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const router = express.Router();
router.use(express.json());
const apiLimiter = require('../middleware/ratelimiter');

//import modules
const youtube = require('./youtube');
const ip = require('./ip');
const sendgrid = require('./sendgrid');
const sendinblue = require('./sendinblue');
const login = require('./login');
const forgotPassword = require('./forgotpassword');
const register = require('./register');
const test = require('./test');

require('dotenv').config();
router.use(helmet());
router.use(morgan('combined'));
router.use(cors());


//this part was added in order to try to fix the cors error
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader(
        'Access-Control-Allow-Headers',
        'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization'
    );
    res.setHeader(
        'Access-Control-Allow-Methods',
        'GET, POST, PUT, DELETE, PATCH, OPTIONS'
    );
    next();
});

app.use(bodyParser.json());

router.use('/ip', ip);
router.use('/youtube', youtube);
router.use('/sendgrid', sendgrid);
router.use('/sendinblue', sendinblue);
router.use('/login', login);
router.use('/register', register);
router.use('/test', test);
router.use('/forgotpassword', forgotPassword);

app.use('/api', router);
// path must route to lambda

app.set('trust proxy', 1);

module.exports = app;
module.exports.handler = serverless(app);

forgotpassword接口代码

const express = require('express');
const router = express.Router();
//const { check, validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const connect = require('../functions/db');
const captcha = require('../middleware/captcha');
const sendMail = require('../functions/sendResetMail');

require('dotenv').config();
// get usermodel
const User = require('../models/User');
const Token = require('../models/resetPassword');

//@path: /forgotpassword
//@ public

router.post(
    '/',
    captcha,
     async (req, res) => {
        const { email } = req.body.data;
        console.log(email);
        connect();
        try {
            // See if user exists
            const user = await User.findOne({ email });
            if (!user) {
                console.log('no user'); //fjernes
                return res.status(200).json({
                    errors: [{ msg: 'If user Exists, an email will be sendt' }],
                });
            }
            const extToken = await Token.findOne({ userId: user._id });
            if (extToken) {
                await extToken.deleteOne();
            }
            const payload = {
                user: {
                    id: user._id,
                },
            };
            const secret = process.env.JWT_RESET_PASSWORD;
            const webToken = jwt.sign(payload, secret, {
                algorithm: 'HS256',
                expiresIn: 3600,
            });

            const salt = await bcrypt.genSalt(10);
            const tokenHash = await bcrypt.hash(webToken, salt);

            const token = new Token({
                userId: user._id,
                token: tokenHash,
            });
            await token.save();
            res
                .status(200)
                .header('Access-Control-Allow-Origin', 'http://localhost:60427')
           // several ways is tried of the above
                .json({ link: webToken, url: UrlLink });
            return;
        } catch (err) {
            res.status(400).json({ errors: [{ msg: err }] });
            return;
        }
    }
);

解决方案

1. 修正CORS中间件加载顺序

当前代码中CORS中间件顺序混乱,导致部分请求未被正确处理。统一使用cors包配置,并将其放在所有中间件最前面:

'use strict';
const express = require('express');
const serverless = require('serverless-http');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const router = express.Router();
const apiLimiter = require('../middleware/ratelimiter');

require('dotenv').config();

// 优先加载CORS中间件,明确指定允许的源
app.use(cors({
  origin: ['http://localhost:8888', 'https://your-frontend-domain.com'],
  credentials: true,
  allowedHeaders: ['Origin', 'X-Requested-With', 'Content', 'Accept', 'Content-Type', 'Authorization'],
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
}));

// 处理所有OPTIONS预检请求
app.options('*', cors());

app.use(helmet());
app.use(morgan('combined'));
app.use(bodyParser.json());
router.use(express.json());

// 导入路由模块
const youtube = require('./youtube');
const ip = require('./ip');
const sendgrid = require('./sendgrid');
const sendinblue = require('./sendinblue');
const login = require('./login');
const forgotPassword = require('./forgotpassword');
const register = require('./register');
const test = require('./test');

router.use('/ip', ip);
router.use('/youtube', youtube);
router.use('/sendgrid', sendgrid);
router.use('/sendinblue', sendinblue);
router.use('/login', login);
router.use('/register', register);
router.use('/test', test);
router.use('/forgotpassword', forgotPassword);

app.use('/api', router);

app.set('trust proxy', 1);

module.exports = app;
module.exports.handler = serverless(app);

2. 确保错误响应携带CORS头

当数据库或bcrypt操作抛出错误时,错误响应也需要携带CORS头,修改forgotpassword接口的catch块:

catch (err) {
  res
    .status(400)
    .json({ errors: [{ msg: err.message }] });
  return;
}

3. 解决Netlify函数超时问题

Netlify函数默认超时10秒,耗时操作可能导致请求被终止,进而缺失CORS头:

  • 在Netlify控制台的函数设置中,将超时时间延长至最大90秒
  • 优化bcrypt参数:将genSalt(10)调整为genSalt(8),减少加密耗时
  • 优化数据库连接:确保connect()复用连接,而非每次请求重新建立

4. 验证环境变量配置

确认Netlify部署时,JWT_RESET_PASSWORD、数据库连接字符串等环境变量已正确配置,环境变量缺失会导致数据库操作失败,间接引发CORS问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:48:46