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

React客户端无法接收Express服务器Cookie的部署问题求助

跨域部署后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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:35:55