The JWT vs. session debate has been going on for a decade. Both approaches authenticate users. They differ in where state lives and what trade-offs you accept.

Server-Side Sessions

The traditional approach: the server stores session data, the client holds a session ID in a cookie.

Client                           Server
  |-- POST /login --------------->|
  |<-- Set-Cookie: sid=abc123 ----|  (stores session in Redis/DB)
  |                                |
  |-- GET /api/me (Cookie: sid) ->|
  |<-- { user: "sam" } -----------|  (looks up session by ID)

Advantages:

  • Revocation is instant. Delete the session from the store and the user is logged out.
  • Small cookie size. Just an opaque ID.
  • Server controls the data. Session content never leaves the server.

Disadvantages:

  • Requires a session store. Redis, database, or in-memory. This is a dependency that needs to be available and fast.
  • Sticky sessions or shared store. In a multi-server setup, every server needs access to the same session data.

JSON Web Tokens (JWTs)

The client holds a signed token containing the user’s identity and claims:

Client                           Server
  |-- POST /login --------------->|
  |<-- { token: "eyJhbG..." } ---|  (signs a JWT)
  |                                |
  |-- GET /api/me                 |
  |   Authorization: Bearer eyJ.. |
  |<-- { user: "sam" } -----------|  (verifies signature, reads claims)

Advantages:

  • Stateless. No session store needed. Any server can verify the token.
  • Works across services. Microservices can verify the token independently.
  • Self-contained. The token carries user ID, roles, and other claims.

Disadvantages:

  • Revocation is hard. You can’t invalidate a JWT before it expires without maintaining a blocklist (which is basically a session store).
  • Token size. JWTs are large (1KB+) and included in every request.
  • Security surface. Storing tokens client-side (localStorage) is vulnerable to XSS. Storing in cookies requires proper flags.

The Practical Answer

For most web applications, server-side sessions are simpler and more secure. Use them unless you have a specific reason not to.

Use JWTs when:

  • You have a microservices architecture where services need to independently verify identity
  • You need stateless authentication at the edge (CDN, Cloudflare Workers)
  • You’re building an API consumed by third parties

If you use JWTs:

  • Short expiry. 15 minutes for access tokens.
  • Refresh tokens. Stored server-side, used to issue new access tokens.
  • HttpOnly cookies. Don’t put tokens in localStorage.
  • Rotate signing keys. Have a key rotation strategy.

The right answer depends on your architecture, not ideology.