How to invalidate JWTs before they expire
JWT statelessness problem, token blacklist, short expiry strategy, token versioning in DB, logout and token invalidation, jti claim
The Stateless Problem
JWTs are stateless — your server does not store them. This means you cannot simply delete a token to invalidate it. If a user logs out or you want to force all sessions to end (after a password change), you have a problem.
Strategy 1: Short Expiry
Keep access tokens short (15 minutes). A stolen token is only valid for a brief window. This is the simplest approach and requires no database. Combined with refresh token rotation, it covers most use cases.
Strategy 2: Token Blacklist
On logout, store the token's jti (JWT ID) claim in a database or Redis with a TTL equal to the token's remaining validity. On each request, check the blacklist. This is the most complete solution but adds latency.
// When signing, include a jti
const token = jwt.sign(
{ sub: user.id, jti: crypto.randomUUID() },
SECRET,
{ expiresIn: '15m' }
);
// On logout, add jti to blacklist with TTL
await redis.set(`blacklist:${payload.jti}`, '1', 'EX', 900);
// On each request, check blacklist
const revoked = await redis.get(`blacklist:${payload.jti}`);
if (revoked) return res.status(401).end();
Strategy 3: Token Version
Store a tokenVersion integer per user in the database. Include it in the token payload. On each request, compare the token's version to the database version. Incrementing the DB version instantly invalidates all existing tokens for that user.
