Skip to main content

JWT vs Session Cookies: Which Should Your App Actually Use?

· 6 min read

JWT got popular because it scales horizontally without a shared session store. That's the headline. But in practice, most teams that pick JWT default to it without considering the tradeoffs. Here's the version you can sketch on a whiteboard.

What each one actually is

Session cookie. The server stores session state (user ID, roles, etc.) keyed by a random session ID. The cookie just carries the ID. The server is the source of truth.

JWT. The server signs a payload containing the user's claims (id, roles, exp, etc.) with a private key. The signed token IS the credential. The server can verify it without looking anything up.

When sessions win

  • You can revoke instantly. Delete the row in your session store and the user is logged out everywhere. JWT revocation requires a blocklist, which defeats the "no lookup needed" benefit.
  • Your sessions are long-lived. A 30-day session in a JWT is a 30-day window where a stolen token grants full access. A 30-day session cookie can be invalidated server-side at any moment.
  • You only have one backend. The horizontal-scaling argument doesn't apply.

When JWT wins

  • Many independent services. Microservices that need to verify identity without calling back to an auth service benefit from self-contained tokens.
  • Short-lived access tokens with refresh. Use a 5-minute JWT access token + a long-lived refresh token. Revocation is bounded by the access token TTL.
  • Mobile and SPA clients on different domains. Cookies need careful CORS/SameSite config; bearer tokens in headers are simpler.

What to put in the payload

Never put secrets in a JWT — anyone holding the token can decode it. (Try our JWT Decoder.) Put only:

  • `sub`: the user ID
  • `exp`: expiration time
  • `iat`: issued-at time
  • minimal authorization claims (roles, tenant)

If the payload starts to grow, you're probably trying to avoid a database lookup that you should just do.

Decision framework

Pick sessions unless you have a specific reason to pick JWT. The default-to-JWT mindset has bitten many teams when they discovered they couldn't revoke tokens, couldn't change permissions mid-session, or shipped sensitive claims in the payload.

If you do pick JWT, keep access tokens short (5-15 min) and use refresh tokens for renewal. The combination gives you JWT's stateless verification with session-cookie-level revocation guarantees.