The JWT vs. session debate has been going on for a decade. Both approaches authenticate users. They differ in where state lives and what trade-offs you accept.
Server-Side Sessions
The traditional approach: the server stores session data, the client holds a session ID in a cookie.
Client Server
|-- POST /login --------------->|
|<-- Set-Cookie: sid=abc123 ----| (stores session in Redis/DB)
| |
|-- GET /api/me (Cookie: sid) ->|
|<-- { user: "sam" } -----------| (looks up session by ID)
Advantages:
- Revocation is instant. Delete the session from the store and the user is logged out.
- Small cookie size. Just an opaque ID.
- Server controls the data. Session content never leaves the server.
Disadvantages:
- Requires a session store. Redis, database, or in-memory. This is a dependency that needs to be available and fast.
- Sticky sessions or shared store. In a multi-server setup, every server needs access to the same session data.
JSON Web Tokens (JWTs)
The client holds a signed token containing the user’s identity and claims:
Client Server
|-- POST /login --------------->|
|<-- { token: "eyJhbG..." } ---| (signs a JWT)
| |
|-- GET /api/me |
| Authorization: Bearer eyJ.. |
|<-- { user: "sam" } -----------| (verifies signature, reads claims)
Advantages:
- Stateless. No session store needed. Any server can verify the token.
- Works across services. Microservices can verify the token independently.
- Self-contained. The token carries user ID, roles, and other claims.
Disadvantages:
- Revocation is hard. You can’t invalidate a JWT before it expires without maintaining a blocklist (which is basically a session store).
- Token size. JWTs are large (1KB+) and included in every request.
- Security surface. Storing tokens client-side (localStorage) is vulnerable to XSS. Storing in cookies requires proper flags.
The Practical Answer
For most web applications, server-side sessions are simpler and more secure. Use them unless you have a specific reason not to.
Use JWTs when:
- You have a microservices architecture where services need to independently verify identity
- You need stateless authentication at the edge (CDN, Cloudflare Workers)
- You’re building an API consumed by third parties
If you use JWTs:
- Short expiry. 15 minutes for access tokens.
- Refresh tokens. Stored server-side, used to issue new access tokens.
- HttpOnly cookies. Don’t put tokens in localStorage.
- Rotate signing keys. Have a key rotation strategy.
The right answer depends on your architecture, not ideology.