Two Ways to Remember Who You Are
HTTP forgets you after every request. After you log in, the server needs a way to recognise you on the next request. The two classic approaches:
- Sessions: the server stores your login and gives your browser a random ID in a cookie.
- JWTs (JSON Web Tokens): the server gives you a signed token containing your identity, and doesn't store anything.
Developers argue about this constantly. The honest answer: both work, they fail in different ways, and most modern apps use a mix.
🎯 Quick Answer (30-Second Read)
- Sessions: stateful. Easy to log someone out instantly. Needs a shared session store when you have many servers.
- JWTs: stateless. Any server can verify them without a lookup. Hard to revoke before they expire.
- For a typical web app: sessions in HttpOnly, Secure cookies are simpler and safer.
- For APIs, mobile apps and microservices: short-lived JWT access tokens plus a revocable refresh token is the common pattern.
- Don't store tokens in localStorage if you can avoid it: any XSS bug can steal them.
How Each One Works
A JWT has three parts, header.payload.signature, each base64url-encoded:
{ "sub": "42", "role": "admin", "iat": 1793000000, "exp": 1793000900 }The payload is signed, not encrypted. Anyone can read it, so never put secrets in a JWT.
Head-to-Head
| Sessions | JWTs | |
|---|---|---|
| Server state | Stored (DB, Redis) | None needed to verify |
| Logout / revoke | Instant: delete the session | Hard: token valid until it expires |
| Scaling | Shared session store needed | Any server verifies independently |
| Size per request | Small cookie ID | Larger token on every request |
| Cross-service use | Each service needs the store | Easy, with the public key |
| Common risk | CSRF (if cookies misconfigured) | Theft from localStorage via XSS, long expiry |
The Revocation Problem
The standard fix is two tokens:
short: 5–15 min"] L --> RT["Refresh token
long-lived, stored server-side,
revocable"] AT -->|"expires"| R["POST /refresh"] RT --> R R -->|"still valid?"| AT2["New access token"] R -->|"revoked / banned"| X["Log out"]
- Access tokens are verified without a lookup, so APIs stay fast.
- Refresh tokens are checked against a store, so you can revoke them.
- Damage from a stolen access token is limited to a few minutes.
Where to Store Tokens in the Browser
| Storage | XSS can steal it? | CSRF risk? | Verdict |
|---|---|---|---|
| HttpOnly, Secure, SameSite cookie | No | Low with SameSite=Lax/Strict + CSRF protection | ✅ Best for web apps |
| localStorage / sessionStorage | Yes | No | ⚠️ Avoid for long-lived tokens |
| In memory (JS variable) | During the session | No | OK for short-lived access tokens |
Set-Cookie: sid=a91f...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=604800Which Should You Choose?
- Server-rendered or Next.js web app, one backend: sessions (or your auth provider's cookie-based session).
- Public API, mobile app, many microservices: short-lived JWT access tokens + refresh tokens.
- Using Supabase, Auth0, Clerk or similar: they typically issue JWTs and manage refresh for you. Follow their recommended cookie setup for SSR.
JWT Security Checklist
- Use strong signing: RS256/ES256 (asymmetric) or HS256 with a long random secret
- Reject the
nonealgorithm and pin the expected algorithm on verify - Always check
exp,issandaud - Keep access tokens short-lived
- Never put passwords or sensitive data in the payload
- Rotate refresh tokens on use and revoke on logout
Frequently Asked Questions
Is JWT more secure than sessions?
Not inherently. Both are secure when implemented correctly. Sessions make instant logout and revocation easy. JWTs avoid server lookups but are harder to revoke, and they're often stored insecurely in localStorage. Storage and expiry choices matter more than the format.
Can you log out a user with JWT?
Deleting the token from the client logs the user out locally, but the token stays valid until it expires if someone copied it. Use short-lived access tokens with revocable refresh tokens, or maintain a server-side denylist for critical cases.
Should I store JWT in localStorage or cookies?
Prefer HttpOnly, Secure, SameSite cookies, which JavaScript can't read, so XSS attacks can't steal the token. localStorage is readable by any script on the page. If you use cookies, add CSRF protection, which SameSite settings largely provide.
Conclusion
Sessions keep state on the server and make logout easy. JWTs carry state in a signed token and scale without lookups, but are hard to revoke. For most web apps, cookie-based sessions are the simplest safe choice. For APIs and mobile, use short-lived JWT access tokens with revocable refresh tokens, and store tokens in HttpOnly cookies whenever you can.
Sources
- RFC 7519: JSON Web Token (JWT)
- OWASP Session Management Cheat Sheet
- OWASP JSON Web Token Cheat Sheet
Last verified: 2 November 2026.
Related reads: Password Hashing Explained · Fix Supabase RLS Policy Error · Idempotency Explained · Rate Limiter Explained
I write practical guides for developers. Follow on LinkedIn or X, or invite me to speak.