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.
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:
Set-Cookie: sid=Jq8x…; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1209600Each 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
| Situation | Choose |
|---|---|
| A web app talking to its own backend | Sessions in an HttpOnly cookie |
| Service-to-service calls, or many APIs behind a gateway | Short-lived signed tokens |
| Third parties calling your API | OAuth 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
HttpOnlycookies can't be read by JavaScript, so an XSS bug can't steal them. SetSameSite=LaxorStrictto block most CSRF, and add a CSRF token for state-changing requests if you need cross-site forms.- Avoid
localStoragefor 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.