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
| Alg | Type | Key | When to use |
|---|---|---|---|
HS256 | HMAC-SHA256 | Single shared secret | Single service, or you fully trust the verifier |
HS384 / HS512 | HMAC-SHA384/512 | Shared secret | Same as HS256; larger signatures rarely worth it |
RS256 | RSA-SHA256 | Public/private key pair | Multiple verifiers (microservices, SSO) |
ES256 | ECDSA P-256 | Public/private key pair | Same as RS256, shorter signatures |
EdDSA (Ed25519) | Edwards-curve DSA | Public/private key pair | Modern default; fast, small, no RSA footguns |
none | No signature | None | Never in production |
Three practical rules for 2026:
- 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.
- 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.
- Reject
alg: noneandalg: HS256on 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:
- 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.
- Revocation list in Redis. Put token IDs (
jticlaim) 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. - 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.
- 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
sessionstable 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
HttpOnlyandSameSite; 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
emailclaim in a JWT lets your frontend render “logged in as X” without an extra/mecall. 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.