JWT vs Session Auth: When to Use Which (with examples)
"Should I use JWT or session cookies for auth?" remains one of the most-asked questions in web development. The internet is full of contradictory advice. This is the practitioner's answer based on what actually works in 2026.
The two approaches in one paragraph
Session auth: on login, server creates a record in a database/Redis with a random session ID. Sends the ID to the client as an HTTP-only cookie. On every request, server looks up the session in storage to identify the user.
JWT auth: on login, server creates a signed token containing the user's claims (id, role, expiry). Sends the token to the client (cookie or localStorage). On every request, server verifies the signature and reads claims from the token — no database lookup needed.
The fundamental tradeoff
Session auth is easy to revoke (delete the row in Redis) but requires a network/cache round-trip per request. JWT auth is stateless and fast but hard to revoke before expiry.
When session auth wins
- You need instant logout across all devices ("revoke this user now")
- You can afford a Redis lookup per request (latency <1ms within the same datacenter)
- Your auth state changes frequently (role changes, permission changes mid-session)
- You're building a traditional monolithic web app with server-side rendering
When JWT auth wins
- You have a distributed/microservices architecture — services don't share a session store
- You serve mobile or third-party API clients where cookies are awkward
- You can tolerate revocation latency (10-15 minutes typical)
- You're optimising for edge runtime auth (Cloudflare Workers, Vercel Edge) — no Redis
The hybrid (best of both)
Most production systems in 2026 use a hybrid:
- Short-lived JWT access token (15 minutes) — used on every request
- Long-lived refresh token stored as HTTP-only cookie + server record
- When access token expires, client uses refresh token to get a new one
- Logout = invalidate the refresh token in DB + token version bump in JWT to force re-login
Session auth example (Express + Redis)
// Session middlewareconstsession =require('express-session');constRedisStore =require('connect-redis')(session); app.use(session({ store:newRedisStore({ client: redisClient }), secret: process.env.SECRET, cookie: { httpOnly:true, secure:true, sameSite:'lax'} }));// Loginapp.post('/login',async(req, res) => {constuser =awaitauthenticate(req.body); req.session.userId = user.id; res.json({ ok:true}); });
JWT example (edge runtime)
// On loginimport{ SignJWT }from'jose';consttoken =await newSignJWT({ uid: user.id, role: user.role }) .setProtectedHeader({ alg:'HS256'}) .setExpirationTime('15m') .setIssuedAt() .sign(newTextEncoder().encode(secret));// On every requestconst{ payload } =awaitjwtVerify(token, secret);constuserId = payload.uid;
JWT security pitfalls (the famous list)
- Storing JWTs in localStorage. XSS-vulnerable. Use HTTP-only cookies.
- The "alg: none" attack. Some libraries accept tokens with
alg: none. Always specify algorithm in verifier config. - Algorithm confusion (RS256 vs HS256). If verifier accepts both, attacker can sign with the public key. Lock to one algorithm.
- Long expiries. 30-day JWTs can't be revoked. Keep access tokens at 15 minutes.
- Leaking sensitive data in payload. JWT claims are base64-encoded, not encrypted. Don't put PII or secrets in claims.
Decode and inspect tokens
Use our JWT decoder to inspect any token — view header, payload, expiry, and verify signature structure. Essential for debugging.
Refresh token rotation
Best practice in 2026: refresh token rotation. Each time a refresh token is used, issue a new one and invalidate the old. If the old token is reused, that's evidence of theft — invalidate the entire family.
The correct boring choice
For most B2B web apps with a single backend, session cookies in Redis are the right answer. Easy to revoke, well-understood, fast on a hot session. Don't reach for JWT to feel modern — reach for it when you actually need statelessness.
For edge runtimes specifically
If you're on Cloudflare Workers or similar, JWT is the path of least resistance. Even then: keep access tokens short (15 min), maintain a small KV-stored revocation list for emergency invalidation.
Bottom line
Sessions are easier to operate. JWTs are easier to scale. Hybrid (short JWT + refresh) is what most production systems converge to. Don't overthink it for an MVP — pick sessions, swap later if needed.
Try the free dev tools suite
15+ tools. 100% client-side. No signup. No tracking.
Browse all tools →