Skip to main content

JWT vs Session Token

The JWT vs session token debate is fundamentally about where state lives. Session tokens (server-side sessions): the token itself is a random opaque string (e.g., a UUID or a cryptographically secure random value). The server stores the actual session data (user ID, permissions, expiry) in a database or cache (Redis, Memcached). On every request the server looks up the token in the store to retrieve session state. Revocation is instant – delete the session from the store and all future requests with that token are immediately rejected. JWT tokens: the token itself carries all claims. The server verifies the cryptographic signature and reads the claims from the payload without any database lookup. This enables stateless horizontal scaling – any server instance can verify any token. The tradeoff: since the server does not store JWTs, revocation requires a token denylist (which reintroduces database lookups for every request) or waiting for the token to expire. The 'stateless JWT' myth: many teams choose JWTs specifically to avoid storing tokens, then add a denylist to support logout, and end up with the database lookup they were trying to avoid – without the simplicity of traditional sessions.

JWTs are self-contained, cryptographically signed tokens that carry all claims the server needs without a database lookup. Session tokens are opaque random strings that the server maps to session state in a database or cache. JWTs scale horizontally with no shared state; session tokens enable immediate revocation. The right choice depends on whether revocability or stateless scalability matters more for your use case.

FeatureJWTSession Token
Token formatSigned JWT: header.payload.sig (Base64URL)Opaque random string (UUID or random bytes)
State locationClient carries all state in the tokenServer stores state; token is just a lookup key
Server lookupNone – signature verified locallyDatabase/cache lookup on every request
RevocationHard – requires denylist or short expiryInstant – delete session from store
Horizontal scalingEasy – any server verifies any tokenRequires shared session store (Redis/DB) across servers
Token sizeTypically 300–600 bytesTypically 32–64 bytes
TransportAuthorization: Bearer header or cookieCookie (HttpOnly, Secure, SameSite) or header
Claim visibilityPayload visible to anyone who decodes itOpaque – no data exposed in token itself
Expiry enforcementEnforced by exp claim in tokenEnforced by TTL in session store
Multi-service authNatural – any service with the public key verifiesRequires shared session store or service-to-service token exchange
StandardRFC 7519 (JWT), RFC 8725 (Best Practices)No formal standard – implementation varies

When to use JWT

JWTs are the right choice for: microservice architectures where multiple services must validate the same token independently, federated identity (OIDC ID Tokens, OAuth 2.0 Bearer tokens), short-lived access tokens (15–60 minutes) where waiting for expiry is acceptable, and stateless API gateway validation at the edge. Use RS256 or ES256 so verifier services never need the signing secret.

When to use Session Token

Server-side sessions are the right choice for: web applications where immediate logout matters (banking, healthcare, admin panels), single-server or shared-store deployments, long-lived sessions (days/weeks) where revocability is critical, and any scenario where you need to enumerate or audit all active sessions for a user. Sessions stored in HttpOnly+Secure+SameSite=Strict cookies are resistant to XSS token theft.

Common Mistakes

  • Storing sensitive data in JWT payload – the payload is Base64URL encoded, not encrypted. Anyone who intercepts the token can decode the payload. Never put passwords, PII, or secrets in a JWT unless using JWE (JSON Web Encryption, RFC 7516).
  • Using long-lived JWTs without a denylist – a 24-hour JWT cannot be revoked when a user logs out or an account is compromised. Keep access token lifetimes short (15–60 minutes) and use refresh tokens with rotation.
  • Storing JWTs in localStorage – localStorage is accessible to JavaScript and vulnerable to XSS. Store JWTs in HttpOnly cookies (inaccessible to JavaScript) when possible. Use the Authorization: Bearer header pattern for SPAs that cannot use cookies.
  • Choosing JWT just to avoid a database – then adding a denylist for logout support, ending up with a database lookup on every request AND more complex token handling than a simple session.

FAQ

Can I use JWTs as session tokens?

Yes, but understand the tradeoffs. A JWT stored in an HttpOnly cookie behaves similarly to a session cookie in terms of transport security. The difference is the server-side: with JWTs there is no central revocation without a denylist, whereas session tokens can be invalidated instantly.

Which is more secure: JWT or session tokens?

Neither is inherently more secure. Security depends on implementation. Session tokens in HttpOnly+Secure+SameSite cookies are resistant to XSS. JWTs are vulnerable to XSS if stored in localStorage. Both are vulnerable to CSRF if not protected. Properly implemented, both are secure.

Why do OAuth 2.0 and OIDC use JWTs?

Because OAuth 2.0 access tokens are consumed by multiple resource servers that should not share a session database. A JWT allows any resource server to independently verify the token using the issuer's public key (from JWKS). This is the key architectural reason JWTs dominate in federated auth.