I am building a MERN blog app. Initially it was running fine before deploying to vercel. The server was hosted on http://localhost:3000 and the client react part was hosted on http://localhost:8000. The server file was deployed on vercel. Other routes are working fine , but the routes which requires cookies (token) are not working. When i see in the request headers, the cookie is not being send. But when i do it before deployment on vercel , it sends the cookies as well to the backend. Why is the error?
The vercel url for backend is : https://blog-app-server-red.vercel.app/
Code for frontend (react) :
const getProfilePic = async ()=>{
try {
const response = await fetch('https://blog-app-server-red.vercel.app/user/'+userId , {
credentials : 'include',
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
}
})
Code for backend (express , mongo) :
app.get('/user/:id' , async (req, res)=>{
try {
const {token} = req.cookies;
const {id} = req.params;
if(token){
jwt.verify(token , process.env.JWT_KEY , {} , async (err , info)=>{
if(err){
throw "Wrong Token ! Access Denied"
}
const user = await User.findById(id).select('-password');
res.status(200).json(user);
});
}
else{
res.status(400).json({error : "Unauthorized !!!"})
}
} catch (error) {
res.status(400).json({error});
}
})
CORS middleware (backend) :
app.use(cors({credentials : true , origin : 'http://localhost:3000'}));
When the same is done for http://localhost:8000 (backend api) , it works fine and the cookie is also getting send. WHy is it?
This issue seems due to CORS and cookie policies when your backend is deployed on Vercel.
Try these configuration to make sure cookies are sent correctly on server,
const allowedOrigins = ['http://localhost:8000', 'https://your-frontend-domain.vercel.app'];
app.use(cors({
credentials: true,
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
Make sure the cookies set by backend server endpoint have the appropriate attributes to be sent in cross-origin requests. You need to set the SameSite
attribute to None
and ensure the Secure
attribute is set to true
for secure contexts (HTTPS)
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'None'
});
On your frontend code all looks good, adding try-catch for error handling would be good.
const getProfilePic = async () => {
try {
const response = await fetch('https://blog-app-server-red.vercel.app/user/' + userId, {
credentials: 'include',
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
}
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
return data;
} catch (error) {
console.error('There was a problem with your fetch operation:', error);
}
};
Only if you are using sessions, make sure the session configuration allows cross-origin cookies,
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: {
secure: true, // Ensure this is true if using HTTPS
sameSite: 'None'
}
}));
your frontend and backend are both should served over HTTPS when in production. Browsers block cookies that are flagged as Secure
when sent over non-secure connections.
Use the browser’s developer tools to verify that the cookie is being set and sent correctly. Check under Application > Cookies to ensure the token is stored correctly and under Network > Headers to see if the cookie is included in the request.