Session secret and store
Two defaults to change here, and both are things you inherit by following the package’s own quick-start rather than things you configure wrongly.
The store
Section titled “The store”express-session’s documentation is blunt about its default:
Warning The default server-side session storage,
MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.
“Purposely” is the word to notice. This is not a limitation to work around — the default exists to make the quick-start work, and shipping it is the mistake.
The security consequence is not just memory. A store that does not scale past one process means sessions break the moment you run two instances, and the usual field fix is sticky sessions or a shorter session lifetime, neither of which is a security decision anyone made deliberately.
The secret
Section titled “The secret”secret signs the session ID cookie. The README’s example value is
'keyboard cat', and it appears verbatim in a great many deployed applications
because it is what you get by copying the working example.
import session from "express-session";import { RedisStore } from "connect-redis";
app.use( session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET, // throw at boot if unset resave: false, saveUninitialized: false, cookie: { secure: true, httpOnly: true, sameSite: "lax" }, }));saveUninitialized: false is worth setting deliberately: the default true writes a
session record for every visitor who has not logged in, which on a public site means
your store fills with empty sessions from crawlers.
node -e "process.env.SESSION_SECRET || (console.error('SESSION_SECRET unset'), process.exit(1))"Better still, verify at boot. Reading process.env.SESSION_SECRET and throwing when
it is missing turns a silent weak-secret deploy into a failed one.
Warning: connect.session() MemoryStore is not designed for a production environment, as it will leak memory, and will not scale past a single process.express-session is running with no store configured. Sessions live in the process, so they vanish on restart and are not shared between instances.
Read in: express-session — emitted at startup when no store option is givenThe MemoryStore warning goes to stderr at startup, which is exactly where it gets lost in container logs. It is worth grepping for on a deploy you did not set up yourself.
Rotating secret invalidates every existing session at once — everyone is logged
out. That is correct if the secret leaked, and disruptive if you are merely tidying
up, so pick your moment. express-session accepts an array of secrets, where the first
signs and the rest still verify, which is the equivalent of Django’s
SECRET_KEY_FALLBACKS and lets you rotate without the mass logout.
Moving off MemoryStore means sessions now depend on an external service being reachable. That is a real availability trade — a Redis outage becomes a login outage — and it is the right trade, but it needs the store treated as a production dependency rather than a cache.
Related
Section titled “Related”- Session cookie flags — the other half of session security
- If your session store is now a Redis you just stood up, it needs its own authentication and network binding — an unauthenticated session store holds every session in the application.