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
相关产品推荐
相关产品推荐

