Cybersecurity
JWT vs session cookies: which should you use for authentication?
Server-side sessions and JSON Web Tokens compared on revocation, scaling, storage, CSRF and XSS risk, with a clear recommendation for typical web apps.
By Raktim Ranjit · Published · 3 min read
Short answer: for a typical web application where the browser talks to your own backend, use server-side sessions with a secure cookie. They are simpler, can be revoked instantly and keep secrets out of the browser. Use JWTs when you need stateless verification across services, short-lived access tokens, or tokens for non-browser clients, and keep them short-lived.
How does a session cookie work?
After login, the server creates a random session ID, stores the session data on the server (in memory, Redis or a database), and sends the ID in a cookie. On each request the browser sends the cookie back, and the server looks up the session. The cookie holds no user data, only an unguessable reference.
Set-Cookie: sid=9f2c...e1; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400How does a JWT work?
A JSON Web Token is a signed string with three parts: header, payload (claims such as user ID and expiry) and signature. The server signs it after login. Later, any service holding the key can verify the signature and trust the claims without a database lookup.
A JWT is signed, not encrypted by default. Anyone can read the payload, so never put secrets in it.
What is the real difference?
- Revocation. A session is deleted on the server and is dead at once. A JWT stays valid until it expires, unless you keep a deny list, which brings back the server-side state you wanted to avoid.
- Lookup cost. Sessions need a store lookup per request. JWTs verify with a signature check.
- Size. A session cookie is tiny. A JWT with claims can be a kilobyte on every request.
- Data freshness. Claims in a JWT, like a role, are stale until the token expires. A session reads current data.
- Cross-service use. JWTs are convenient when many services must verify the caller without sharing a session store.
Where do you store a JWT in a browser?
This is where people get hurt. localStorage is readable by any JavaScript on the page, so one cross-site scripting bug steals the token. A cookie marked HttpOnly is not readable by scripts, which is why storing a token in such a cookie is safer. At that point you are using a cookie with a different payload, and many of the sessions' benefits apply anyway.
What about CSRF and XSS?
- Cookies are sent automatically by the browser, so you need CSRF protection:
SameSite=LaxorStrict, plus CSRF tokens for state-changing requests where needed. - Tokens in headers are not sent automatically, so CSRF does not apply, but the token must live somewhere scripts can reach, which exposes it to XSS.
Neither choice removes the need to prevent XSS. A strict Content Security Policy helps, though it can break apps if misconfigured, as I found with Next.js hydration.
What are common JWT mistakes?
- Accepting
alg: noneor letting the token choose its algorithm. Pin the algorithm on the server. - Weak or hard-coded signing secrets.
- Very long expiry, like 30 days, with no way to revoke.
- Putting personal data or permissions that change often in the payload.
- Not checking
exp,issandaud.
What is a sensible hybrid?
Use a short-lived access token (5 to 15 minutes) and a longer-lived refresh token stored server-side or in an HttpOnly cookie, rotated on each use. A stolen access token expires fast, and the refresh token can be revoked. This is the pattern behind most OAuth setups. See OAuth vs OIDC vs SAML for how tokens fit with third-party login.
Which should you pick?
- Single web app plus its own API: sessions.
- Mobile app or public API clients: tokens, short-lived, with refresh.
- Many internal services verifying identity: JWT with short expiry.
- You are unsure: sessions. You can add tokens later for the places that need them.
References
Author
Raktim Ranjit is a software engineer and the founder of NodeDR Infotech. He builds and maintains the software described here.