Skip to content

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.

MeasureSessionsJWT
Store load20,000 / 100,000 = 20%none
Memory500,000 x 1 KB = 500 MBnone
Extra bytes per second20,000 x 32 = 640,000 = 0.64 MB20,000 x 300 = 6 MB
CPU for checksthe lookup cost20,000 x 0.05 ms = 1,000 ms = 1 core
Stolen phonenext request failsworks 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. HttpOnly stops page scripts from reading it. Secure sends it only over HTTPS. SameSite stops 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 HttpOnly is safer.

Sources

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.