For Express sessions, use a server side store, rename the default cookie, set secure and httpOnly, and configure trust proxy if you sit behind one. For logins, limit failures by username plus IP and by IP alone, using two different time windows.
Pick the right session middleware
The Express docs describe two options. express-session keeps session data on the server and puts only a session ID in the cookie. It defaults to in-memory storage, so the docs tell you to use a scalable session store in production. cookie-session stores the whole session in the cookie, so use it only for small primitive data, keeping the total under 4093 bytes per domain.
If your session holds anything you would not want a user to read or replay, prefer the server side option.
Cookie settings
The docs list these:
- name: do not keep the default. Set a generic one such as
sessionIdwith thenameoption, so the cookie does not advertise the framework. - secure: send only over HTTPS.
- httpOnly: block access from client JavaScript.
- domain and path: restrict where the cookie is sent.
- expires: set an expiry for persistent cookies.
app.use(session({
name: 'sessionId',
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: { secure: true, httpOnly: true, path: '/', maxAge: 3600000 },
}));
Behind a proxy that terminates TLS, the docs say to set app.set('trust proxy', 1). Without it, a secure cookie may never be set because Express thinks the request is plain HTTP. Only trust the number of proxy hops you actually have.
Brute force protection on login
The Express docs suggest tracking failed attempts two ways:
- Consecutive failures for a username and IP address pair.
- Total failures from an IP address over a longer period. The example blocks an IP after 100 failures in a day.
The rate-limiter-flexible package implements both. Two counters matter because attackers try many passwords for one account, or one password across many accounts, and a single limit catches only one of those.
A review list
- Session secret read from the environment, not source.
- Production session store configured, not memory.
- Cookie flags set and checked in browser tools.
- Proxy hops configured.
- Login failures limited two ways, with logging.
Quick check
Why not keep the default cookie name?
The docs recommend a generic name so the cookie does not reveal which middleware you use.
Is rate limiting by IP alone enough?
No. Pair it with a username plus IP counter, as the docs suggest, so slow targeted guessing is also caught.
Sources
Last reviewed: 19 October 2026.