I set cookies in NodeJS with the following code:
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const max_session_ms = 365 * 24 * 60 * 60 * 1000;
// Initialize mongodb session storage to remember users.
const store = new MongoDBStore({
uri: MONGO_URI,
expires: max_session_ms,
});
// Enable sessions using encrypted cookies
app.use(
session({
cookie: {
maxAge: max_session_ms,
sameSite: "lax",
},
store: store,
secret: "some secret",
signed: true,
resave: true, // Resave when user visits again and extend expiration date.
saveUninitialized: false, // Save only explicitly, e.g. when logging in.
httpOnly: true, // Don't let browser javascript access cookies.
secure: true, // Use cookies over https.
})
);
When I look at the cookie on Safari, connecting to the production server on the web domain with an SSL certificate via HTTPS, HttpOnly is checked but Secure
is not checked:
The Set-Cookie
header that a browser receives (using Firefox set to refuse all cookies) looks like this:
Set-Cookie: connect.sid=s%3Ap06....DBs...; Path=/; Expires=Wed, 18 Mar 2026 10:33:14 GMT; HttpOnly; SameSite=Lax
Furthermore, MongoDB is saving cookies for every visit, e.g. bots, when saveUninitialized: false
should save them only for authenticated users. MongoDB now has 1.5 million sessions without a user for only 1 thousand sessions with a user.
Am I using the cookie settings correctly? If so, how should I debug this issue?
I tried with CURL, curl -v https://ginja.org
, and I don't see a Set-Header
cookie in the response because it redirects to another page. If I use cURL
with the destination URL, i.e. curl -v https://ginja.org/pt/inicio
, then I see a cookie header:
< content-type: text/html; charset=utf-8
< x-powered-by: Express
< set-cookie: connect.sid=s%3AEmB...Dk5.pLlK...Cs4; Path=/; Expires=Tue, 24 Mar 2026 19:41:28 GMT; HttpOnly; SameSite=Lax
< cf-cache-status: DYNAMIC
The issue you're experiencing is a common misunderstanding about how saveUninitialized: false works with express-session and flash messages.
The problem is that flash messages automatically initialize sessions. When you use flash messages (like req.flash()), the session middleware creates a session to store the flash data, even when saveUninitialized: false is set. This happens because:
Flash messages need somewhere to be stored temporarily
The session becomes "modified" when flash data is written to it
Modified sessions are always saved, regardless of the saveUninitialized setting
Why Your Settings Are Correct But Not Working
Your session configuration is actually correct:
app.use(session({
cookie: {
maxAge: max_session_ms,
sameSite: "lax",
},
store: store,
secret: "some_secret",
signed: true,
resave: true,
saveUninitialized: false, // This should prevent saving uninitialized sessions
httpOnly: true,
secure: true,
}));
However, saveUninitialized: false only prevents saving sessions that are:
Created but never modified
Don't contain any data
Once you call req.flash() or write any data to req.session, the session becomes "initialized" and will be saved.
To confirm this is the issue:
Test without flash messages: Temporarily remove all req.flash() calls and test if sessions are still created for unauthenticated users.
Add logging: Log when sessions are created
app.use(session({
// ... your config
}));
app.use((req, res, next) => {
const wasNew = !req.session.id;
next();
if (wasNew && req.session.id) {
console.log('New session created:', req.session.id);
console.log('Session data:', req.session);
}
});
3. Check session contents: Log what's in the sessions being created to confirm they contain flash message data