I've spent days trying to resolve this issue but haven't had any luck. I've read almost all questions and articles related to this problem, but nothing has worked.
Setup My frontend and backend are deployed on a Contabo VPS using Docker Compose.
The frontend (Next.js) is hosted on .
The backend (Node.js + Express) is hosted on . In development (localhost), the frontend runs on port 3000 and the backend on port 5000. I’m using cookies for authentication. The Problem
✅ In localhost: When both the frontend and backend run on localhost, cookies are sent correctly, and protected API requests work as expected.
❌ In production: When the frontend is on and the backend is on , cookies are not being sent, resulting in 401 Unauthorized errors. Even if I try to access deployed backend via its domain from localhost frontend still same error.
Steps to Reproduce Go to and log in with: Email: [email protected] Password: naru After logging in, visit and check the Network tab in DevTools. You’ll see API requests failing because the cookies are not included in the request headers.
Here is how my backend CORS and cookies code looks like.
const allowedOrigins = [
"http://localhost:3000",
"http://localhost:3001",
";,
];
const corsOptions = {
origin: function (origin, callback) {
// Check if the origin is in the allowed list or if there is no origin (like in some cases of server-to-server requests)
if (allowedOrigins.indexOf(origin) !== -1 || !origin) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true, // Allow credentials (cookies, authorization headers, TLS client certificates)
};
// Use the CORS middleware with the configured options
app.use(cors(corsOptions));
And cookies code
export const loginUser = async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
const tokenExpiry = new Date(Date.now() + 24 * 60 * 60 * 1000);
if (!user) {
return res.status(401).json({
status: "error",
message: "User not found please create account",
});
}
const hashPassword = CryptoJS.AES.decrypt(
user.password,
process.env.PASS_SEC
);
const originalPassword = hashPassword.toString(CryptoJS.enc.Utf8);
originalPassword !== req.body.password &&
res.status(401).json("Wrong credentials!");
const accessToken = jwt.sign(
{
id: user.id,
isAdmin: user.admin,
},
process.env.JWT_SEC,
{ expiresIn: "24h" }
);
const { password, ...others } = user._doc;
if (req.cookies[`${user._id}`]) {
req.cookies[`${user._id}`] = "";
}
res.cookie(String(user._id), accessToken, {
expires: tokenExpiry,
httpOnly: true,
secure: true,
sameSite: "none",
domain: ".ikhlaas.pk",
});
res.status(201).json({ ...others, accessToken, admin: user.admin });
} catch (err) {
res.status(500).json(err);
}
};
frontend API calling code
async function makeGetRequest(
url: string,
config: AxiosRequestConfig<any> = {},
withToken = false
): Promise<AxiosResponse | undefined> {
try {
const response = await apiClient.get(url, {
...config,
// withCredentials: true,
withCredentials: withToken ? true : false,
});
return response;
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
return Promise.reject(error.response);
}
return Promise.reject(error);
}
}
const apiClient = axios.create({
baseURL: getUrl(),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
withCredentials: true,
});
export function getUrl() {
return ";;
}