node.jsmongodbexpresscookiessession-cookies

NodeJS cookie settings (HTTPS, saveUninitialized) fail to apply


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:

cookie settings on Safari

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?

update

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

Solution

  • The issue you're experiencing is a common misunderstanding about how saveUninitialized: false works with express-session and flash messages.

    Root Cause

    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:

    1. Flash messages need somewhere to be stored temporarily

    2. The session becomes "modified" when flash data is written to it

    3. 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:

    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:

    1. Test without flash messages: Temporarily remove all req.flash() calls and test if sessions are still created for unauthenticated users.

    2. 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