React客户端无法接收Express服务器Cookie的部署问题求助
问题背景
前后端分别部署在不同服务器:后端(Express)在Render,前端(React)在Vercel。本地开发正常,但部署后出现以下问题:
- 登录接口返回成功(控制台无报错)
- 客户端无法接收/存储Cookie
- 首页因检测不到Cookie,自动跳转回登录页
已排查项:
- axios请求已设置
withCredentials: true - 后端CORS已配置
Access-Control-Allow-Credentials: true - 后端
res.cookie已设置withCredentials: true
相关代码
Express服务器登录函数
const Login = async (req, res, next) => { try { const { email, password } = req.body if (!email || !password) { return res.json({ message: "All fields required" }) } const user = await User.findOne({ email }) if (!user) { return res.json({ message: "Incorrect email or password" }) } const auth = await bcrypt.compare(password, user.password) if (!auth) { return res.json({ message: "Incorrect email or password" }) } const token = createSecretToken(user._id); res.cookie("token", token, { withCredentials: true, httpOnly: false, }); res.status(201).json({ message: "User logged in successfully", success: true }); next() } catch (error) { console.log(error) } }
Express服务器index.js
const express = require('express'); const mongoose = require('mongoose'); const bcrypt = require('bcrypt') const cookieParser = require("cookie-parser"); const bodyParser = require("body-parser") const path = require('path') const cors = require('cors') require('dotenv').config(); const app = express(); const classRoutes = require('./routes/courseRoutes') const authRoute = require("./routes/AuthRoutes"); const PORT = process.env.PORT || 4000; mongoose.connection.on('connected', () => { console.log('Connected to MongoDB Atlas'); }); app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "https://divcourses.vercel.app"); res.header("Access-Control-Allow-Credentials", "true"); res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization"); next(); }); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); app.use('/api', classRoutes) app.use('/api', authRoute); app.use((req, res, next) => { console.log(`${req.method} ${req.url}`); next(); }); const atlasConnectionUri = process.env.MONGODB_URL; mongoose.connect(atlasConnectionUri, { dbName: 'subjects' }); app.get('/', async (req, res) => { try { res.status(200).json({ message: "Welcome to Home Route 🏠" }) } catch (error) { res.status(500).json({ message: "Error in Home Route ❌" }) } }); app.listen(PORT, () => { console.log(`Server is Running at ${PORT}`); });
React登录组件
import React, { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import axios from "axios"; import { ToastContainer, toast } from "react-toastify"; const Login = () => { const navigate = useNavigate() const [inputValue, setInputValue] = useState({ email: "", password: "" }) const { email, password } = inputValue const handleOnChange = (e) => { const { name, value } = e.target setInputValue({ ...inputValue, [name]: value }) } const handleError = (err) => toast.error(err, { position: "bottom-left", }); const handleSuccess = (msg) => toast.success(msg, { position: "bottom-left", }); const handleSubmit = async (e) => { e.preventDefault() try { const { data } = await axios.post( "https://mern-deploy-practice.onrender.com/api/login", { ...inputValue }, { withCredentials: true } ) console.log(data); const { success, message } = data; if (success) { handleSuccess(message); setTimeout(() => { navigate("/"); }, 1000); } else { handleError(message); } } catch (error) { console.log(error); } setInputValue({ ...inputValue, email: "", password: "", }); };
React首页组件
import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useCookies } from "react-cookie"; import { Link } from "react-router-dom"; import axios from "axios"; import { ToastContainer, toast } from "react-toastify"; const Home = () => { const [cookies, setCookies, removeCookie] = useCookies(['token']); const [username, setUsername] = useState('') const navigate = useNavigate() useEffect(() => { const verifyCookie = async () => { try { const response = await axios.post( "https://mern-deploy-practice.onrender.com/api", {}, { withCredentials: true } ); if (response && response.data) { const { status, user } = response.data; setUsername(user) setCookies('token', cookies.token) const greeted = localStorage.getItem('greeted'); if (!greeted) { toast(`Hello ${user}`, { position: "top-right" }); localStorage.setItem('greeted', 'true'); } } else { console.error(error) removeCookie("token"); navigate('/login'); } } catch (error) { console.error("Error verifying cookie:", error); removeCookie("token"); navigate('/login'); } if (!cookies.token) { navigate("/login"); return; } }; verifyCookie(); }, [cookies, navigate, setCookies, removeCookie]); const Logout = () => { removeCookie("token"); navigate("/signup"); };
网络请求截图
- 响应头:

- 请求头:

解决方案
1. 修复后端Cookie跨域配置
登录函数中res.cookie缺少跨域必需的属性,修改如下:
res.cookie("token", token, { httpOnly: true, // 开启后避免XSS攻击,axios仍可自动携带Cookie secure: true, // 必须开启,SameSite=None仅支持HTTPS环境 sameSite: 'None', // 跨域场景强制设置为None,允许第三方Cookie maxAge: 24 * 60 * 60 * 1000, // 可选,设置Cookie有效期1天 });
2. 优化CORS配置
替换手动设置响应头的方式,使用cors包确保配置严谨:
// 删除原来的手动header中间件,替换为以下代码 app.use(cors({ origin: "https://divcourses.vercel.app", // 严格匹配前端域名 credentials: true, // 允许携带凭证(Cookie) methods: ["GET", "HEAD", "OPTIONS", "POST", "PUT"], allowedHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization"] }));
3. 修正前端Cookie读取逻辑
首页组件中无需手动同步Cookie,useCookies会自动处理,调整代码:
useEffect(() => { const verifyCookie = async () => { try { const response = await axios.post( "https://mern-deploy-practice.onrender.com/api", {}, { withCredentials: true } ); if (response.data.status) { setUsername(response.data.user); const greeted = localStorage.getItem('greeted'); if (!greeted) { toast(`Hello ${response.data.user}`, { position: "top-right" }); localStorage.setItem('greeted', 'true'); } } else { removeCookie("token"); navigate('/login'); } } catch (error) { console.error("Error verifying cookie:", error); removeCookie("token"); navigate('/login'); } }; // 先检查本地Cookie,不存在直接跳转登录 if (!cookies.token) { navigate("/login"); return; } verifyCookie(); }, [cookies, navigate, removeCookie]);
4. 确保前端根组件包裹CookieProvider
在App.js中添加CookiesProvider,否则useCookies无法正常工作:
import { CookiesProvider } from 'react-cookie'; import { BrowserRouter } from 'react-router-dom'; function App() { return ( <CookiesProvider> <BrowserRouter> {/* 你的路由和组件 */} </BrowserRouter> </CookiesProvider> ); } export default App;
内容的提问来源于stack exchange,提问作者TeDi
相关产品推荐
相关产品推荐

