express-session: server-side sessions for Express, and where MemoryStore stops working
Simple session middleware for Express
At a glance
- What is it?
- express-session stores session data on the server and puts only a signed session ID in the cookie. This review covers the cookie options that decide security, the MemoryStore warning in the README, and the path-matching change in 1.19.1.
- Who is it for?
- Adopt express-session if you are building an Express app that needs server-side session state and you are prepared to attach a production store, because the README states the default MemoryStore is not designed for production, leaks memory under most conditions and does not scale past a single process.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 28 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What express-session actually solves for an Express app
HTTP is stateless. A browser that logs in and then requests a second page arrives with no memory of the first request, so the server has to attach an identity to the client somehow. express-session does that by issuing a session ID, signing it, and setting it as a cookie. The README is explicit about the split: session data is not saved in the cookie itself, only the session ID, and session data is stored server-side. That single design decision explains most of the project's behaviour and most of its operational cost.
The audience is Node.js developers building on Express who want session state they control on the server: a user ID, a cart, a flash message, a CSRF token. Because the state lives server-side, you can invalidate a session by removing it from the store rather than waiting for a token to expire. That is the trade you are making. You gain server-side control and you take on a store to run, plus a lookup on every request that carries a session cookie.
It is not a general authentication framework. There is no user model, no password handling, no role system. express-session answers one question: which session does this request belong to, and what data is attached to it.
How the middleware reads and writes the session cookie
The middleware wraps req and res. On each request it looks for the session cookie, unsigns it, and loads the matching record from the configured store into req.session. When the response is about to be written, the middleware decides whether to persist changes and whether to send a Set-Cookie header, using the on-headers dependency listed in package.json. The session ID itself is generated with uid-safe.
A note in the README matters for anyone migrating an older app: since version 1.5.0, cookie-parser is no longer needed, because express-session reads and writes cookies on req and res directly. The README warns that using cookie-parser alongside it may cause issues if the secret is not the same between the two. That is a real failure mode rather than a theoretical one, because a mismatched secret produces a session that silently fails to load.
The cookie defaults are stated in the README as { path: '/', httpOnly: true, secure: false, maxAge: null }. Read that carefully: secure defaults to false, and maxAge defaults to null, meaning no maximum age. Both defaults are convenient in development and both need attention before production. The README also notes that if both expires and maxAge are set, the last one defined in the object wins, and that expires should not be set directly; use maxAge instead.
Installing express-session and getting a first session working
The README gives the install command directly. It is a normal npm package with no build step and no native dependencies.
npm install express-sessionThe module is required as session and used as Express middleware. The README's own example passes a secret, disables resave and enables saveUninitialized, which is the combination most tutorials repeat.
var session = require('express-session')
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))With that in place, req.session is available inside your route handlers. Writing a property to it marks the session as modified, and the middleware persists it before the response headers go out. The browser receives a Set-Cookie header carrying the signed session ID, and sends it back on subsequent requests.
The README also documents a callback form for the cookie option, which is worth knowing about because it is not obvious from the default object. The callback receives req and returns the cookie settings for that request, which lets you vary path, secure and maxAge per request instead of once at startup.
cookie: function(req) {
var match = req.url.match(/^\/([^/]+)/);
return {
path: match ? '/' + match[1] : '/',
httpOnly: true,
secure: req.secure || false,
maxAge: 60000
}
}That example is the README's, and it shows the intended use: derive cookie scope from the request rather than hard-coding it.
MemoryStore is the default and the README says not to ship it
The strongest warning in the README concerns the default store. MemoryStore is described as purposely not designed for a production environment, leaking memory under most conditions, not scaling past a single process, and being meant for debugging and developing. That is unusually direct language for a README, and it should be read as the project's own position on its default configuration.
The consequence is practical. A single-process Express app in development will work fine with no store configured. The moment you run two Node processes behind a load balancer, a session created on one process is invisible to the other, and users appear to be logged out at random. Nothing in the middleware prevents this; the failure appears in production as intermittent authentication loss, which is expensive to diagnose after the fact.
A second constraint follows from the same design. Every request that carries a session cookie triggers a store lookup. With a remote store such as a database or Redis, that is a network round trip on the request path. express-session does not batch or cache those lookups for you. The README points readers to a list of compatible session stores rather than bundling one, so choosing and operating that store is your responsibility, not the middleware's.
There is also a behavioural detail worth flagging for anyone upgrading. The README states that since 1.19.1, cookie path matching follows RFC 6265 section 5.1.4, so the middleware only activates when the request path is an exact match or falls under a segment boundary of the cookie path. A cookie path of /admin matches /admin and /admin/users but not /administrator. Earlier versions used a simple prefix check without segment boundaries. An app that relied on the looser behaviour, deliberately or by accident, will see sessions stop loading on paths that previously matched.
Cookie options that decide whether the session is safe to expose
The cookie settings do more security work here than the middleware logic does. The README documents each one, and the defaults are not all safe for a public site.
httpOnly is true by default, so client-side JavaScript cannot read the cookie through document.cookie. That protects the session ID from a large class of script-injection theft, and the README notes the flip side: if you set it to true, compliant clients will not expose the cookie to client-side JavaScript, so any front-end code reading it will break.
secure is false by default. On a site served over HTTPS, leaving it false means the session cookie can travel over a plain HTTP connection if one is available, which is exactly the condition session hijacking needs. The README documents sameSite with values of true, false, 'lax', 'none', 'strict' and 'auto', where 'auto' sets None for secure connections and Lax for non-secure ones. It also notes a draft spec requiring Secure to be true when SameSite is 'none', and that some browsers may adopt it. That combination is where misconfiguration tends to surface.
Two attributes are documented as not fully standardized. partitioned sets the Partitioned attribute and is tied to the CHIPS proposal; the README says many clients may ignore it until they understand it. priority accepts 'low', 'medium' or 'high', with medium as the default when unset, and carries the same caveat. Treating either as a security control today is premature. They are hints to clients that may or may not act on them.
Where express-session is the wrong tool, and what to compare it with
If your application only needs to prove that a request came from a client that authenticated earlier, and you do not need to revoke that proof before it expires, a stateless signed token is a simpler fit. The difference is architectural rather than a matter of quality. A signed token carries its claims in the token itself, so any process can verify it with a shared secret and no store lookup happens on the request path. express-session keeps the data server-side and looks it up, which costs a round trip but gives you immediate revocation: delete the record and the session is gone.
That difference decides the choice. Multi-process deployments with a shared Redis or database store are where express-session fits naturally, because the store already exists and the lookup is cheap. Edge or serverless deployments where each request may run in a fresh isolate are a poor fit, because MemoryStore cannot be shared and a per-request in-memory store is not a session at all. Batch jobs and internal tools behind a single process can use MemoryStore, but the README's warning still applies to anything long-running, since it leaks memory under most conditions.
Within the store question, the choice is not between express-session and a competitor but between stores. The README keeps a list of compatible stores instead of bundling one, so the decision is operational: what you already run, what your latency budget allows, and who pages when it goes down.
Maintenance, upgrades and the MIT licence
The repository is not archived, and the last push was on 2026-09-01. Recent releases are v1.19.0 on 2026-01-22, v1.18.2 on 2025-07-17 and v1.18.1 on 2024-10-08. The gap between 1.18.2 and 1.19.0 is roughly six months, so releases arrive when there is something to ship rather than on a schedule. Version 1.19.0 is what package.json declares, and it is the version you get from a plain npm install.
Upgrade cost is low in normal cases. The dependency list is small and stable, and the engines field declares node >= 0.8.0, which is permissive enough that almost any current runtime satisfies it. The one upgrade that needs attention is the path-matching change described for 1.19.1, because it alters when the middleware activates rather than how it behaves once active. If your app sets cookie.path to anything other than '/', test the boundary cases after upgrading.
The licence is MIT, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are included. That is a permissive licence with no copyleft obligation on your application. It is not legal advice; if your organisation has specific compliance requirements, have counsel review the LICENSE file in the repository, which is the authoritative text.
Editorial conclusion
Adopt express-session if you are building an Express app that needs server-side session state and you are prepared to attach a production store, because the README states the default MemoryStore is not designed for production, leaks memory under most conditions and does not scale past a single process. Do not adopt it if you only need a stateless signed token, since express-session keeps state on the server and therefore needs a shared store once you run more than one process. Before shipping, verify three things: that you replaced MemoryStore, that cookie.secure is true behind your TLS terminator, and that your cookie.path values behave as expected under the RFC 6265 section 5.1.4 matching introduced in 1.19.1, which no longer treats /administrator as a match for /admin.
Frequently asked questions
How do I install express-session?
Install it from npm with npm install express-session, then require it as session and register it with app.use. The README notes it is a Node.js module available through the npm registry and needs no build step.
Does express-session store session data in the cookie?
No. The README states that session data is not saved in the cookie itself, only the session ID, and that session data is stored server-side.
Can I use express-session in production with the default store?
The README warns that the default MemoryStore is purposely not designed for a production environment, will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing. A production deployment needs a different store from the compatible stores list.
Do I still need cookie-parser with express-session?
No. Since version 1.5.0 the module reads and writes cookies on req and res directly, so cookie-parser is not required. The README warns that using it anyway may cause issues if the secret differs between the two.
What changed about cookie path matching in express-session?
Since 1.19.1, path matching follows RFC 6265 section 5.1.4, so the middleware activates only on an exact match or a segment boundary of the cookie path. A cookie path of /admin matches /admin and /admin/users but not /administrator, whereas earlier versions used a simple prefix check.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/expressjs-session)