Skip to content
JWT in 2026 — What Changed and What to Use Instead

Web & HTTP

JWT in 2026 — What Changed and What to Use Instead

JWT is still everywhere but its edges are sharper than most teams realize. Algorithm choices, revocation tradeoffs, and when opaque session tokens beat JWTs outright.

JWT remains the default answer when someone says “stateless auth”, and it is still right for some jobs. It is also still wrong for many of the jobs teams use it for. This piece is a 2026 status update: what to keep using, what to stop using, and when to skip JWT entirely.

What JWT actually gives you

A JWT is three Base64url-encoded segments joined by dots: header.payload.signature. The header names the algorithm; the payload is JSON claims; the signature is computed over header.payload using a key.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjMiLCJleHAiOjE3MzQ1NjcwMDB9.
_signature_bytes_base64url_

That structure gives you exactly one property: the server can verify that the payload was signed by a party holding the key, without a database lookup. Everything else (sessions, authorization, SSO) is layered on top of that single primitive.

For the encoding detail itself, see the Base64 in production guide — JWTs use Base64url without padding, which is why they do not survive a round-trip through a naive standard-Base64 decoder.

The algorithm choices that actually matter

AlgTypeKeyWhen to use
HS256HMAC-SHA256Single shared secretSingle service, or you fully trust the verifier
HS384 / HS512HMAC-SHA384/512Shared secretSame as HS256; larger signatures rarely worth it
RS256RSA-SHA256Public/private key pairMultiple verifiers (microservices, SSO)
ES256ECDSA P-256Public/private key pairSame as RS256, shorter signatures
EdDSA (Ed25519)Edwards-curve DSAPublic/private key pairModern default; fast, small, no RSA footguns
noneNo signatureNoneNever in production

Three practical rules for 2026:

  1. Use asymmetric signatures when more than one service verifies the token. HS256 is attractive because it is simple, but once you share the secret with three services you have three places it can leak. RS256 or EdDSA let issuer and verifiers have different keys.
  2. Prefer EdDSA (Ed25519) for new systems. It is faster than RSA, signatures are 64 bytes, and you avoid RSA’s key-size and padding-mode footguns. Support is now universal in JOSE libraries.
  3. Reject alg: none and alg: HS256 on a public-key verifier. The classic “alg confusion” attack is to send a token signed with HMAC using the verifier’s RSA public key as the HMAC secret. Modern libraries default to rejecting this, but pin the expected algorithm explicitly, not just “any algorithm the library accepts”.
// Node jose library — pin the algorithm, do not let the token pick it
import { jwtVerify, importSPKI } from 'jose';

const publicKey = await importSPKI(pem, 'EdDSA');
const { payload } = await jwtVerify(token, publicKey, {
  issuer: 'https://auth.example.com',
  audience: 'api.example.com',
  algorithms: ['EdDSA'], // explicit allow-list
});

The revocation problem

JWT’s marquee feature is “stateless verification”: no database lookup. That is also its biggest operational problem. A signed token is valid until it expires. If a user logs out, or a password is compromised, or a device is lost, you cannot instantly invalidate the token without giving up statelessness.

Four typical responses, in increasing order of infrastructure cost:

  1. Short expiration + long refresh tokens. Access tokens live 5-15 minutes; refresh tokens live weeks and live in a database where they can be revoked. This is the OAuth 2.0 pattern and it is the right default.
  2. Revocation list in Redis. Put token IDs (jti claim) of revoked tokens in Redis with a TTL matching the token’s remaining life. Verifier checks Redis on every request. You have now re-introduced a database lookup — but only a cheap one, and only for revoked tokens.
  3. Per-user epoch. Every user has an integer in the database; tokens carry the epoch; logout increments it. Verifier compares the token’s epoch to the user’s current epoch. One DB lookup per request — same cost as session lookup.
  4. Give up and use sessions. The moment you are doing a DB lookup on every request anyway, the statelessness story is gone. Opaque random tokens + a sessions table are simpler, smaller, and easier to revoke.

When to not use JWT

A short checklist. If any of these is true, opaque session tokens in a database are probably a better fit:

  • You are building a standard web app with a single backend. There is no federation, no multi-service verification. You just need to know who the user is.
  • You need instant logout or “kill all sessions” as a product feature.
  • You are storing the token in a cookie anyway. Cookies give you HttpOnly and SameSite; a bare random token is smaller, safer, and trivially revocable.
  • You were about to put personally identifying information in the payload, because “the payload is signed”. It is signed, not encrypted. Anyone with the token can decode it.

Session tokens (opaque random strings) + HttpOnly; Secure; SameSite=Lax cookies + a sessions table is boring, proven, and solves the 80% case with no token rotation dance.

Where JWT still wins

  • Federation and SSO. OIDC ID tokens are JWTs by definition. If you speak OIDC, you speak JWT.
  • Service-to-service authentication inside a trust boundary. Short-lived JWTs signed by a single issuer, verified by many services, is a tidy pattern.
  • Client-side decoding for UI hints. A public email claim in a JWT lets your frontend render “logged in as X” without an extra /me call. Just remember the signature does not protect against client tampering of the display; treat it as a hint, not a source of truth for authorization.

Two implementation details people miss

Leeway on exp and nbf. Clocks drift. Every JOSE library lets you configure a leeway window (default 0-30s). Set it to something; do not ship a verifier that rejects tokens because of 2s of clock skew across your own data center.

Rotate keys. Asymmetric keys should rotate on a schedule (90-365 days is common). Publish the current and previous public keys so tokens signed under the old key still verify during rollover. JWK Sets (JWKS) handle this; if you are hand-rolling verification, you will eventually paint yourself into a corner.

For the broader picture of how UUIDs and tokens differ as opaque identifiers, see the UUID v4 vs v7 piece; for hash and password-hashing choices that anchor the underlying auth flow, see the hash functions comparison.

Takeaways

Use JWT when you need cross-service or cross-party verification without a shared database. Use opaque session tokens for the single-app case. Pin the algorithm, prefer EdDSA for new systems, never put secrets in the payload, and plan for revocation before you need it.

By ·