Concept · Web services

Sessions vs tokens

A random ID the server looks up, or a signed token the server verifies — and what each costs at logout.

4 minJWTsession cookiebearer tokenstateless auth

After a user logs in, every later request has to prove who they are. There are two common designs.

Server-side sessions

The server creates a random, unguessable session ID, stores session_id → user_id, expiry in a database or Redis, and sends the ID in a cookie:

http
Set-Cookie: sid=Jq8x…; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1209600

Each request looks the ID up.

  • Revocable instantly: delete the row and the user is logged out everywhere.
  • Small cookie that holds no data.
  • Costs a lookup per request. That's cheap in Redis, and the lookup is usually not your bottleneck.

Signed tokens (JWTs)

The server signs a token containing the claims (sub, exp, roles) and the client sends it on each request, usually as Authorization: Bearer …. Any server holding the verification key can check it without a lookup.

  • Good across services and organisations: an API gateway or a third party can verify it with a public key.
  • Hard to revoke. A stolen token stays valid until exp. Teams therefore keep access tokens short-lived (5–15 minutes) and pair them with a long-lived refresh token that the server does store and can revoke. At that point you're keeping server-side state again.
  • Everyone can read it. A JWT is signed, not encrypted, so never put secrets in it.

Choosing

SituationChoose
A web app talking to its own backendSessions in an HttpOnly cookie
Service-to-service calls, or many APIs behind a gatewayShort-lived signed tokens
Third parties calling your APIOAuth access tokens (often JWTs)

"JWTs make the server stateless" is mostly a myth for user-facing apps. You'll want logout, "sign out other devices" and banning, and all of them need state.

Storage in the browser

  • HttpOnly cookies can't be read by JavaScript, so an XSS bug can't steal them. Set SameSite=Lax or Strict to block most CSRF, and add a CSRF token for state-changing requests if you need cross-site forms.
  • Avoid localStorage for tokens. Any injected script can read it.

Whatever you choose, verify the password properly first, and rotate the session ID at login to prevent session fixation.

Learn it in depth