security-best-practices
December 22, 2025

When NOT to Use JWT Authentication

JWT isn't always the answer. Learn when to avoid JWT authentication and use sessions instead for better security.

7 min readReal-time tracking enabled
When NOT to Use JWT Authentication
Share:

JWT (JSON Web Tokens) have become incredibly popular for API authentication, and it's easy to see why. They're stateless, portable, and seem like the perfect modern solution. But here's the thing: JWT isn't always the right choice, and using it in the wrong scenarios can create more problems than it solves.

Let's talk about when you should actually avoid JWT and what you should use instead.

The Token Revocation Problem

This is probably the biggest issue with JWT authentication. Once you issue a JWT, you can't really take it back. Think about it like handing someone a physical pass card. Even if you fire them, that card still works until it expires.

The fundamental challenge is that server-side revocation isn't straightforward with JWTs. When a user logs out or when you need to immediately revoke access (like when an employee leaves your company), the JWT continues to be valid until its expiration time.

Sure, you can implement a blacklist or a token revocation system, but here's the catch: once you start maintaining a blacklist, you're storing state on the server. You're querying a database or cache on every request to check if a token is revoked. At that point, you've essentially defeated the main advantage of JWTs, which is fast, stateless verification.

If you need immediate logout capabilities or the ability to instantly revoke access, JWT makes your life harder, not easier.

When Your App Manages Sensitive User Sessions

Let's say you're building a banking application, an admin dashboard, or any system where users need precise control over their active sessions. JWT becomes problematic here.

In session-based authentication, you can immediately terminate a user's access by invalidating their session in the database. With JWT, even if a user changes their password or reports suspicious activity, any valid tokens will continue working until they expire.

This creates security windows that are hard to defend. If someone's JWT gets stolen, they have anywhere from 5 to 30 minutes (or however long your token lifetime is) to wreak havoc, and there's nothing you can do about it without implementing complex workarounds.

Traditional Web Applications with Server-Side Rendering

If you're building a classic web app with server-side rendering where the same server handles both rendering pages and authentication, JWT adds unnecessary complexity.

Traditional web applications benefit from session-based authentication where the server maintains session state and immediate control over sessions is crucial. Sessions were literally designed for this use case.

With sessions, you get:

  • Built-in CSRF protection (when implemented correctly)
  • Automatic cookie handling by the browser
  • Simple logout (just delete the session)
  • No need to manually attach tokens to every request

JWT in this scenario is like using a sledgehammer to hang a picture frame. It works, but it's overkill.

Small to Medium-Sized Applications

Not every app needs to scale to millions of users. If you're building a small to medium-sized application, the "stateless scalability" argument for JWT doesn't hold much weight.

Session management with modern session stores isn't actually a scaling bottleneck for most applications. With Redis or similar in-memory stores, session lookups are incredibly fast. The performance difference is negligible for most real-world applications.

The added complexity of JWT (token refresh logic, handling expiration on the frontend, managing token storage) often isn't worth it for smaller applications where sessions work perfectly fine.

When You Need Fine-Grained Permission Changes

Imagine this: a user's role changes from "editor" to "viewer" in your system. With session-based auth, the next request they make will reflect their updated permissions. With JWT, their token still claims they're an editor until it expires.

When authorization information like roles is embedded in a JWT, changes to user permissions won't be reflected until existing tokens expire. This creates a window where users might have more (or less) access than they should.

You could work around this by making tokens very short-lived and using refresh tokens, but now you're adding complexity. And if you're checking permissions against the database anyway to handle updates, what's the point of putting them in the JWT?

Single-Page Apps with Same-Origin APIs

If your React, Vue, or Angular app talks to an API on the same domain, sessions with HTTP-only cookies might be a better choice than JWT.

Session IDs are meaningless without server-side lookup, and when stored in HTTP-only cookies, they're less exposed to XSS attacks. JWTs stored in localStorage are vulnerable to XSS, and if you store them in cookies instead, you still need CSRF protection.

For same-origin applications, the security benefits of sessions often outweigh the flexibility of JWT.

When You Don't Have Distributed Services

JWT shines in distributed systems where multiple independent services need to verify tokens without talking to a central authority. The major advantage of JWT is that verification can happen independently without needing to contact the identity provider.

But if all your services can access the same session store, or if you're running a monolithic application, this benefit disappears. You're taking on JWT's complexity without getting its main advantage.

The JWT Size Problem

Here's something people don't talk about enough: JWTs can get big, especially when you start adding claims for user information and permissions.

Unlike session cookies which are just small identifiers, JWTs carry the full payload and signature, increasing bandwidth consumption on every request. If you're encoding lots of user data, you might even hit URL or header size limits.

For mobile apps or users on slower connections, this extra overhead adds up quickly.

What Should You Use Instead?

So if JWT isn't the answer, what is? Here are better alternatives for different scenarios:

Classic Sessions with Cookies

Perfect for traditional web apps and same-origin APIs. They're simple, battle-tested, and work great with server-side rendering. Modern session stores like Redis make them fast and scalable.

Stateful Sessions with Short-Lived JWTs

A hybrid approach where you maintain server-side sessions but issue short-lived JWTs (like 5-15 minutes) for actual request authentication. This combines the fast verification of JWTs with the revocation capabilities of sessions. When a JWT expires, check the session validity before issuing a new one.

OAuth 2.0 with Opaque Tokens

For API authentication, consider using OAuth 2.0 with opaque access tokens instead of JWTs. The authorization server tracks token validity, giving you full control over revocation.

API Keys for Service-to-Service Communication

If you're securing communication between your own services in a trusted network, simple API keys might be sufficient and much simpler than JWT.

Decision Guide: Session vs JWT

Here's a quick guide to help you decide:

Choose Sessions when:

  • Building traditional web apps with server-side rendering
  • Immediate logout/revocation is critical
  • Working with sensitive data (banking, healthcare)
  • App scale is small to medium
  • Services share a session store
  • Simplicity is important

Consider JWT when:

  • Building truly distributed microservices
  • Services are completely independent
  • Third-party services need to verify identities
  • Temporary access to external resources (like signed S3 URLs)
  • Doing API-to-API authentication
  • Mobile apps need to authenticate with multiple backends

Wrapping Up

JWT isn't bad, it's just overused. It became popular partly because it has great marketing ("JSON", "Web", "Token" sounds modern and scalable), and partly because there were lots of tutorials showing how to use it without discussing the tradeoffs.

The real issue is that security should be straightforward, but JWT's complexities and limitations make it unsuitable for many common use cases.

Before reaching for JWT, ask yourself: Do I really need stateless authentication? Do I have genuinely distributed services? Can I live with the revocation problem? If the answer to any of these is no, sessions might be the better, simpler choice.

Remember, boring technology that works is almost always better than exciting technology that creates problems. Sometimes the old solutions are old because they work.

References

Enjoyed this article?

Vote or share it with someone who might love it.

George Ongoro
George Ongoro

Blog Author & Software Engineer

I'm George Ongoro, a passionate software engineer focusing on full-stack development. This blog is where I share insights, engineering deep dives, and personal growth stories. Let's build something great!

View Full Bio

Related Posts

Comments (0)

Join the Discussion

Please login to join the discussion