JWT vs session cookies: which auth should you use?
"Sessions vs JWTs" is really a question about where auth state lives: on the server (a session ID pointing at a server-side record) or inside the credential itself (a signed token the server verifies statelessly). Neither is universally better; they trade the same properties in opposite directions.
| Server-side session | JWT | |
|---|---|---|
| State | On the server (memory/Redis/DB) | In the token; server stateless |
| Revocation | Instant — delete the record | Hard — valid until exp unless you add a denylist |
| Horizontal scaling | Needs a shared session store | Any node with the public key can verify |
| Cross-service auth | Awkward — every service hits the store | Natural — services verify locally |
| Size per request | ~32-byte cookie | Hundreds of bytes to several KB |
How each one works
A session cookie is a random ID; everything about the user stays server-side, so nothing sensitive ever leaves and the record can be destroyed at any moment. A JWT carries its claims with it — sub, exp, roles — over a signature, so any service holding the issuer's public key verifies it without a lookup. (Anyone can also read those claims: a JWT is signed, not encrypted.)
The revocation problem
The signature that makes JWTs stateless also makes them irrevocable: logging out, deleting an account, or detecting a stolen token doesn't invalidate copies already issued. The standard mitigation is short-lived access tokens (minutes, enforced by exp) plus a revocable refresh token — see ID vs access vs refresh tokens — and, where you need per-token kill switches, a denylist keyed by jti. Note what happened: you reintroduced server state to fix statelessness. If you end up checking a store on every request anyway, a session would have been simpler.
When each is the right call
- Sessions: a server-rendered or single-backend web app, same-site clients, instant logout as a hard requirement. Boring and correct.
- JWTs: APIs consumed by multiple services, microservices verifying tokens from a shared identity provider, mobile/SPA clients on OAuth 2.0 / OIDC flows, or cross-domain federation.
- Hybrid (very common): a session cookie for the first-party web app, JWTs minted by the same identity provider for API and service-to-service calls.
If you're inheriting a JWT setup and want to see what's actually inside those tokens, decode one in the TokenPrism debugger — it flags weak spots like missing exp or an unexpected algorithm as you paste.