JWT vs sessions: there is no universal answer
·2 min read ·Security · Backend · Authentication
JWT vs sessions is one of those debates where both sides are right, both sides are wrong, and the actual answer depends entirely on your situation. Here's how I think about it.
Sessions: The Sane Default for Web Apps
Session-based auth stores state on the server. The browser holds a session ID in an httpOnly cookie. On each request, the server looks up the session to verify the user.
Advantages:
- Instant revocation: delete the session and the user is logged out everywhere
- No token storage concerns—
httpOnlycookies are inaccessible to JavaScript - CSRF protection is well-understood and built into frameworks
For a standard web application where your frontend and backend share a domain, sessions are the right default. Laravel makes this trivially easy.
JWT: Right for APIs, Wrong When Misused
JWT is stateless—the server doesn't store anything. The token itself contains the claims. This makes it attractive for:
- APIs consumed by mobile apps that can't use cookies
- Microservices that need to pass identity between services
- Cross-domain authentication
The problem is what developers get wrong:
// Don't do this:
localStorage.setItem('token', jwt); // Vulnerable to XSS
// If you must use JWT in a browser, use httpOnly cookies:
// Let the server set it via Set-Cookie header
You also can't revoke a JWT before it expires. If a user changes their password or you suspect compromise, a valid JWT stays valid until expiry. Short TTLs (15 minutes) with refresh token rotation are the mitigation—but that adds complexity.
My Rule
Web app with standard server-rendered or SPA frontend on same domain: sessions.
API consumed by mobile apps, third parties, or cross-domain clients: JWT with short expiry + refresh token rotation.
Never put a JWT in localStorage in a browser. Never make a JWT with a one-year expiry. These are not opinions—they're security mistakes.