Sessions vs JWT vs Cookies: Understanding Authentication Approaches

Your application needs to know who's accessing it. When a user logs in, you need to remember they're authenticated so they don't have to log in again for every request. There are three main ways to do this: sessions, cookies, and JWT tokens. Each has different tradeoffs.
What Are Cookies?
Cookies are small pieces of data stored on the client's browser. When you set a cookie, the browser automatically sends it with every request to your server.
Think of cookies like a stamp on your hand at an amusement park. Once you get the stamp, you show it to staff at each ride. The staff don't need to check your ticket every time.
Here's how cookies work:
Server sends a Set-Cookie header in the response
Browser stores the cookie
Browser automatically includes the cookie in every request back to the server
// Setting a cookie in Express
res.cookie('username', 'john', { httpOnly: true, maxAge: 3600000 });
res.send('Cookie set');
Cookies are just a transport mechanism. By themselves, they don't authenticate anyone. They need to work with either sessions or JWT to be useful.
What Are Sessions?
A session is server-side storage of user information. When a user logs in, the server creates a session and stores data about that user. The server then sends a session ID to the client, which stores it in a cookie.
How session-based authentication works:
User logs in with username and password
Server validates credentials and creates a session
Server stores session data (like user ID) in memory or a database
Server sends session ID in a cookie
Browser includes the session cookie in every request
Server looks up the session ID to find user information
This is stateful authentication. The server maintains state about who's logged in.
import express from 'express';
import session from 'express-session';
const app = express();
app.use(session({
secret: 'your-secret-key',
resave: false,
saveUninitialized: true,
cookie: { maxAge: 1000 * 60 * 60 * 24 } // 24 hours
}));
app.post('/login', (req, res) => {
// Verify user credentials
const userId = 123;
// Store in session
req.session.userId = userId;
res.send('Logged in');
});
app.get('/profile', (req, res) => {
if (req.session.userId) {
res.send(`Welcome user ${req.session.userId}`);
} else {
res.status(401).send('Not logged in');
}
});
What is JWT?
JWT stands for JSON Web Token. It's a self-contained token that includes all the information the server needs to authenticate a user. Instead of storing session data on the server, you encode the data into the token itself.
JWT structure:
A JWT has three parts separated by dots: header.payload.signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header: Specifies the algorithm used to sign the token
Payload: Contains claims (data) like user ID and username
Signature: Ensures the token hasn't been tampered with
Here's how JWT authentication works:
User logs in with username and password
Server validates credentials
Server creates a JWT with user information
Server sends JWT to the client
Client stores JWT (usually in localStorage or a cookie)
Client sends JWT in the Authorization header with each request
Server verifies the signature and extracts user information
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
const SECRET_KEY = 'your-secret-key';
app.post('/login', (req, res) => {
// Verify user credentials
const userId = 123;
const username = 'john';
// Create JWT
const token = jwt.sign(
{ userId, username },
SECRET_KEY,
{ expiresIn: '24h' }
);
res.json({ token });
});
app.get('/profile', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).send('No token');
}
try {
const decoded = jwt.verify(token, SECRET_KEY);
res.send(`Welcome ${decoded.username}`);
} catch (error) {
res.status(401).send('Invalid token');
}
});
Sessions vs JWT: Key Differences
| Aspect | Sessions | JWT |
|---|---|---|
| State | Stateful (server keeps data) | Stateless (data in token) |
| Storage | Server (memory/database) | Client (localStorage/cookie) |
| Scalability | Harder to scale across multiple servers | Easy to scale (no server state) |
| Logout | Easy (delete session) | Harder (token valid until expiry) |
| Bandwidth | Small cookie sent | Larger token sent |
| Mobile | Works with cookies | Better for APIs |
When to Use Each
Use Sessions When:
You're building a traditional web application with server-rendered pages
You need immediate logout capability
You're running on a single server or have a shared session store
You want simpler implementation
Use JWT When:
You're building a mobile app or single-page application
You have multiple servers that need to authenticate users independently
You want a stateless architecture
You're building a public API
Use Cookies When:
You're setting session IDs or tokens
You need automatic browser handling
You're building traditional web apps
Common Security Practices
For Sessions:
Use secure, HttpOnly cookies
Set appropriate session timeout
Regenerate session ID on login
Use HTTPS only
For JWT:
Sign tokens with a strong secret
Use HTTPS to prevent token interception
Set reasonable expiry times
Consider using refresh tokens for longer sessions
Keep the secret safe
Real-World Example Comparison
Let's say you have a simple blogging platform. Here's how each approach handles a request flow:
Session-based flow:
User logs in → Server creates session with user ID
User requests profile → Server checks session, recognizes user
User logs out → Server deletes session
JWT-based flow:
User logs in → Server creates token with user ID
User requests profile → Token sent with request, server verifies signature
User logs out → Client simply deletes token
Key Takeaways
Cookies are just a transport mechanism, not authentication by themselves
Sessions are stateful: server stores user information
JWT is stateless: user information is stored in the token itself
Sessions are easier for traditional web apps and immediate logout
JWT scales better for distributed systems and APIs
Both can use cookies for transport; the difference is what data is stored where
Choose the approach based on your application architecture. Traditional web apps often use sessions, while modern APIs and mobile apps commonly use JWT.



