React前端+Node后端部署Vercel后CORS策略错误求助
问题描述
本地环境下React前端调用Node.js后端API正常,但部署到Vercel后,出现CORS错误:
Access to XMLHttpRequest at 'https://portfolio-5-0-backend.vercel.app/api/send-email' from origin 'https://portfolio-5-0-frontend.vercel.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
用户当前CORS配置及前端API调用代码如下:
后端CORS配置
const allowedOrigins = ["https://portfolio-5-0-frontend-2c3fpno4h-kroplewskims-projects.vercel.app", "https://portfolio-5-0-frontend.vercel.app"]; const corsOptions = { origin: function (origin, callback) { if (!origin || allowedOrigins.indexOf(origin) !== -1) { callback(null, { origin: true }); } else { callback(new Error("Not allowed by CORS")); } }, methods: ["GET", "POST", "PUT", "PATCH", "DELETE"], allowedHeaders: ["Access-Control-Allow-Origin", "Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization"], credentials: false, }; app.options("*", cors(corsOptions)); app.use(cors(corsOptions)); app.use(bodyParser.json());
前端API调用代码
const onSubmit = handleSubmit(async (data) => { try { PushNotification("Sending email...", "bg-info"); const response = await axios.post("https://portfolio-5-0-backend.vercel.app/api/send-email", { Email: data.Email, Name: data.Name, Message: data.Message, }, { withCredentials: true, headers: { 'Content-Type': 'application/json', }, }); if (response.status === 200) { PushNotification("Email sent successfully!...", "bg-success"); } } catch (e) { PushNotification("Failed to send email", "bg-error"); console.log(e); } });
解决方案
1. 修正CORS配置的核心问题
- origin回调格式错误:原代码返回
{ origin: true }不符合cors中间件要求,应直接返回true或匹配的origin值。 - allowedHeaders冗余配置:
Access-Control-Allow-Origin是后端响应头,无需加入请求允许的headers列表。 - credentials不匹配:前端设置
withCredentials: true,后端需同步设置credentials: true,二者保持一致。
修正后的CORS配置:
const allowedOrigins = ["https://portfolio-5-0-frontend-2c3fpno4h-kroplewskims-projects.vercel.app", "https://portfolio-5-0-frontend.vercel.app"]; const corsOptions = { origin: function (origin, callback) { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error("Not allowed by CORS")); } }, methods: ["GET", "POST", "PUT", "PATCH", "DELETE"], allowedHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization"], credentials: true, }; // 保持中间件顺序:先处理OPTIONS请求,再应用CORS,最后解析请求体 app.options("*", cors(corsOptions)); app.use(cors(corsOptions)); app.use(bodyParser.json());
2. 同步前端withCredentials配置
若后端设置credentials: false,需将前端的withCredentials: true改为false,避免凭证不匹配:
const response = await axios.post("https://portfolio-5-0-backend.vercel.app/api/send-email", { Email: data.Email, Name: data.Name, Message: data.Message, }, { withCredentials: false, headers: { 'Content-Type': 'application/json', }, });
3. 适配Vercel Serverless函数场景
如果后端是Vercel Serverless函数(如api/send-email.js),需确保中间件正确挂载:
import cors from 'cors'; import express from 'express'; import bodyParser from 'body-parser'; const app = express(); const allowedOrigins = ["https://portfolio-5-0-frontend-2c3fpno4h-kroplewskims-projects.vercel.app", "https://portfolio-5-0-frontend.vercel.app"]; const corsOptions = { origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: true, }; app.use(cors(corsOptions)); app.use(bodyParser.json()); app.post('/', async (req, res) => { // 邮件发送逻辑 res.status(200).json({ message: 'Email sent successfully' }); }); export default app;
4. 强制刷新Vercel部署缓存
Vercel可能缓存旧构建产物,导致新配置未生效。在Vercel控制台找到后端项目,进入Deployments选择最新部署,点击Redeploy并勾选Clear build cache & deploy后重新部署。
5. 验证环境变量配置(若使用)
如果allowedOrigins从环境变量读取,需确保Vercel控制台的环境变量已正确设置,且部署时选择对应环境。
内容的提问来源于stack exchange,提问作者Mat

