Trade-offs · Foundations
JWT vs server sessions
When to use a signed token (JWT) and when to use a server-side session for login, the numbers that decide, and what breaks with each.
Asked at: SDE-1SDE-2
The decision
After login, every later request must carry proof of who you are. In a session, the server keeps a record and gives the browser a random session ID in a cookie. Each request is checked with a lookup. In a JSON Web Token (JWT), the proof is a signed token that holds the user ID and an end time. Any server with the key can check the signature without a lookup. The trade is a lookup per request against the power to cancel a login at once.
Choose server sessions when
- You run a normal website or app with one login system.
- You must be able to log a user out, or block them, at once. One delete does it.
- You handle money or private data, where a stolen login must stop in seconds.
- You want a small request: a session ID is about 32 bytes.
Choose a JWT when
- Many separate services must check a login, and you do not want each of them to call one store.
- Another party, such as a partner service, must check a login without calling your store.
- Short lifetimes are fine. You can give the token 15 minutes and refresh it.
- You accept that cancelling needs extra work: a refresh record or a denylist, which is a list of cancelled tokens.
The numbers that change the answer
Worked example at Indian scale. A food-ordering app at dinner time. Assumptions: 500,000 users are logged in and send 20,000 requests per second. A session store does 100,000 operations per second. A session record is 1 KB and a session ID is 32 bytes. A token is 300 bytes and lives 900 seconds (15 minutes). Checking one signature takes 0.05 ms.
| Measure | Sessions | JWT |
|---|---|---|
| Store load | 20,000 / 100,000 = 20% | none |
| Memory | 500,000 x 1 KB = 500 MB | none |
| Extra bytes per second | 20,000 x 32 = 640,000 = 0.64 MB | 20,000 x 300 = 6 MB |
| CPU for checks | the lookup cost | 20,000 x 0.05 ms = 1,000 ms = 1 core |
| Stolen phone | next request fails | works until expiry, up to 15 minutes |
The token adds 300 / 32 = 9.4 times more bytes per request. If the token lived 24 hours, the stolen-phone window would be (24 x 60) / 15 = 96 times longer.
The common fix. Give the JWT a 15-minute life and add a long-lived refresh token that the server stores like a session ID. Each user asks for a new token every 900 seconds: 500,000 / 900 = 556 lookups per second. That is 556 / 20,000 = 2.8% of the lookups of the session design. To block a user, delete the refresh record. The thief's window is at most 15 minutes.
Clock check. The server checks the end time with its own clock. A server whose clock is 20 minutes ahead rejects every new 15-minute token. A leeway (extra seconds that the server allows) of 60 seconds does not hide that.
What breaks with each choice
- Sessions: the store is a single dependency. If it is down, nobody can log in or use the app. You must also share it among servers. Fix: run the store with a standby copy.
- JWT: you cannot cancel a token before its end time. A signed token is not secret, because anyone can read its payload, so keep secrets out of it. A server that accepts the signing method
none, or that skips the signature check, is a serious hole. Name the allowed method in your code. - JWT: a denylist brings state back, and every request must check it.
- Both: keep the proof in a cookie with three marks.
HttpOnlystops page scripts from reading it.Securesends it only over HTTPS.SameSitestops other sites from making the browser send it.
What it costs
- Sessions cost a store to run: 500 MB of memory and 20% of its capacity in our example. They are simple to build and to explain.
- A JWT costs less at read time but more in code: refresh tokens, key rotation, clock care. Plan for days to weeks of work (rough guess).
- Changing later is possible. Moving from sessions to JWT is easy, because you add a token and keep the store for refresh. Moving from JWT to sessions logs everybody out once.
How each level answers
SDE-1: A session keeps the proof on the server. A JWT carries the proof in the token.
SDE-2: For a normal website I use sessions, because logout and blocking are one delete. For many services that must check logins alone, I use a short JWT.
Senior: At 20,000 requests per second, sessions use 20% of a 100,000-per-second store. A JWT removes that load but adds 6 MB per second and a 15-minute stolen-token window. A refresh token adds only 556 lookups per second. The risk is a wrong clock, so I keep clocks synced. The fallback is a denylist.
Staff: I ask how fast a stolen login must die, and which teams must verify logins. For one product, I choose sessions. When several teams need to verify logins, I add short JWTs with a refresh token and one team owns key rotation. I check the choice by the time from "block user" to the last accepted request, store load, and 401 errors by server.
Say it in the interview
I would use server sessions for the website, because a stolen login must stop at once and one delete does that. At 20,000 requests per second the store handles 20% load. If many services must verify logins alone, I would add a 15-minute JWT with a refresh token.
Common mistakes
- Saying "JWT is stateless, so it scales better" and ignoring cancel. The lookup was never the hard part for most sites.
- Putting secrets or private data in the payload. It is signed, not encrypted.
- Using a long JWT life, such as 24 hours, with no way to cancel.
- Keeping the token in browser local storage. Any script on the page can read it, so a cookie marked
HttpOnlyis safer.
Sources
- Session ID of 64 bits or more, idle timeouts, and server-side invalidation at logout: OWASP Session Management Cheat Sheet, https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html (opens in a new tab) (checked 26 Oct 2026).
- A denylist to cancel a token, and short expiry to limit reuse: OWASP JSON Web Token Cheat Sheet, https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet.html (opens in a new tab) (checked 26 Oct 2026).
- Not re-fetched today (standard documents): cookie settings (RFC 6265), JWT claims and leeway (RFC 7519), refusing the
nonemethod (RFC 8725). - User counts, request rates, sizes and timings are assumptions made for teaching.
Understand the trade-off. Practise explaining your choice.
In an AI mock interview, the interviewer can push on a choice like this one: it changes a number and asks if your answer still holds.