I'm developing an application with an Express.js backend and a Next.js frontend. My backend uses Redis
and connect-redis
packages for session management, and my server is deployed on Heroku with the Heroku-Data for Redis
add-on.
I'm encountering an issue where session cookies are not being sent from the server to the frontend in a cross-origin context, despite configuring CORS headers to accept credentials.
Here is an excerpt from my server-side (Express.js) configuration:
// app.js
...
// initialize Redis
async function initializeRedis() {
const redisClient = redis.createClient({
url: process.env.REDIS_TLS_URL, // url provided by Heroku-Data for Redis
socket: {
tls: true,
rejectUnauthorized: false,
},
});
redisClient.on("error", function (error) {
console.error("Erreur de Redis :", error);
});
await redisClient.connect().catch(console.error);
return redisClient;
}
// start app
async function startApp() {
/* Redis
-------------------------------------------------- */
const redisClient = await initializeRedis();
const redisStore = new RedisStore({ client: redisClient });
/* Express Session
-------------------------------------------------- */
const sessionConfig = {
store: redisStore,
secret: process.env.EXPRESS_SESSION_SECRET_KEY,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 600000, // 10 minutes
secure: process.env.NODE_ENV === "production",
httpOnly: true,
sameSite: process.env.NODE_ENV === "production" ? "None" : "Strict",
},
};
/* CORS
-------------------------------------------------- */
const cors = require("cors");
const corsOptions = {
origin: "https://www.my-site.app",
methods: ["GET", "POST"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true,
};
...
}
And here's how I'm making the requests on the client-side (Next.js):
// this request creates the session in the backend, storing the order id in the session cookie
const response = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_ENDPOINT}/validate-order`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(order),
credentials: "include",
}
);
// this request is supposed to receive the cookie from the session
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_ENDPOINT}/get-order-id`,
{
method: "GET",
credentials: "include",
}
);
The session cookie is not received by the client when making requests to my API (with my front-end deployed to Vercel with https). No cookie in the response headers. I've checked and I'm sure that the issue is not related to CORS configurations, as the appropriate headers seem to be in place, and cross-origin requests are indeed allowed.
Has anyone encountered this issue or have any idea what might be going wrong ? Is there something specific to Heroku or Redis configuration that I might be missing ?
Thank you in advance for your help.
What I tried :
I can see that the keys are properly created and not empty when I look into my Redis database :
sess:e22dFw38IpASoyv3Hk-xxx
I noticed that I can receive the cookie properly with this configuration of the Express-Session cookie :
secure : false,
sameSite : « Strict »
but only when the request comes from http://localhost:3000 with the server running locally. I don’t receive the cookie when the server is running on Heroku with this configuration.
I updated the maxAge
cookie parameter to 2 hours to make sure the problem wasn't related to timezones
deployed my server on a custom domain and enabled Heroku SSL as mentioned in this thread. Still no cookie on the front-end.
added the domain
parameter to the cookie configuration in app.js
as mentioned here :
const sessionConfig = {
...
cookie: {
...
domain: ".my-site.app", // do this only if your server and your front-end share the same domain, with your server on a subdomain like api.my-site.app
},
};
Issue solved by adding app.set("trust proxy", 1);
to the server as mentioned here.
Cloud services like Heroku often use reverse proxy so this line tells Express that the initial request was made over https even if it was transmitted over http after the proxy.