HTTP, by design, has no memory. Every request a server receives is treated as if it's the very first time it's ever heard from that visitor — which is a problem the moment you need to remember that someone is logged in, or track what's in their shopping cart across multiple page loads. Sessions solve exactly this.
The mechanism, conceptually
- A visitor makes their first request. The server creates a session — a small storage area tied to that one visitor — and generates a unique session ID for it.
- The server sends that session ID back inside a cookie, which the browser stores automatically.
- On every following request, the browser automatically re-sends that cookie.
- The server looks up the session ID from the cookie, finds the matching stored session data, and now "remembers" that visitor.
The data itself lives on the server (or in a store like Redis); only the session ID travels to and from the browser.
Setting it up with express-session
npm install express express-session
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'replace-with-a-long-random-string',
resave: false,
saveUninitialized: false
}));
A visit-counting example
app.get('/', (req, res) => {
if (req.session.visits) {
req.session.visits += 1;
} else {
req.session.visits = 1;
}
res.send(`You've visited this page ${req.session.visits} time(s).`);
});
Refresh the page repeatedly and the count climbs — because req.session persists between requests from the same browser, even though each request is technically a brand-new, otherwise memory-less HTTP call.
A login example
app.get('/login', (req, res) => {
req.session.user = { id: 42, name: 'Rahim' };
res.send('Logged in');
});
app.get('/dashboard', (req, res) => {
if (!req.session.user) {
return res.status(401).send('Please log in first');
}
res.send(`Welcome back, ${req.session.user.name}`);
});
app.get('/logout', (req, res) => {
req.session.destroy(() => {
res.send('Logged out');
});
});
Practical guidelines worth following
- Set
cookie: { secure: true }in production so the session cookie is only ever sent over HTTPS. - Never store sensitive raw data (like a plain-text password) inside a session — only what you genuinely need, like a user ID.
- The default in-memory session store is fine for local development, but it loses everything on restart and can't be shared across multiple server instances — production apps typically point
express-sessionat Redis or a database instead. - Give sessions a reasonable expiry so an abandoned, logged-in browser tab doesn't stay authenticated forever.