JWT
token expiration
authentication
security
web development

How do I handle JWT token expiration?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Handling JWT expiration correctly is mostly about separating access-token lifetime from session lifetime. A short-lived access token limits damage if it leaks, while a longer-lived refresh mechanism allows the user to stay signed in without constant reauthentication. The mistake is not token expiration itself. The mistake is building an expiration flow with no refresh plan or no revocation strategy.

Use Short-Lived Access Tokens

The exp claim tells the server when a token is no longer valid. Access tokens should usually be relatively short-lived.

A typical payload looks like this:

json
1{
2  "sub": "user-123",
3  "iat": 1700000000,
4  "exp": 1700000900
5}

On the server, expired tokens should be rejected consistently. Do not silently accept them with ad hoc grace periods unless you have a clear reason and clock-skew handling policy.

Pair Them with Refresh Tokens

The standard solution is:

  1. short-lived access token
  2. longer-lived refresh token
  3. refresh endpoint that issues a new access token

That lets the application renew session continuity without turning the access token itself into a long-lived credential.

The access token and refresh token should also have different handling rules. Treating them as interchangeable credentials usually leads to a weaker design.

Store Tokens Safely

Where you store the token matters almost as much as how long it lives. A common web pattern is:

  • access token kept in memory
  • refresh token stored in a secure, HTTP-only cookie

That reduces direct JavaScript exposure for the long-lived credential while still allowing session renewal.

Handle Expiration on the Client Predictably

When an API returns 401 because the access token expired, the client should try one controlled refresh flow, then retry the original request if refresh succeeds.

Pseudo-flow:

  1. request fails with 401
  2. client calls refresh endpoint
  3. client stores new access token
  4. original request is retried once

Avoid letting many simultaneous requests all trigger independent refresh attempts. A single-flight refresh strategy is safer.

This is especially important in browser apps with many parallel API calls. Without coordination, several requests can all try to refresh at once and create race conditions in stored auth state.

Add Revocation and Rotation for Refresh Tokens

If refresh tokens are long-lived, they need stronger controls than access tokens. Useful practices include:

  • rotate refresh tokens after use
  • track them server-side
  • revoke them on logout or suspected compromise

Without that, a stolen refresh token can quietly extend a session much longer than intended.

Handle Expiration as a Security and UX Problem

Expiration is not only a backend validation rule. It also affects user experience. If refresh fails, the client should clear auth state and send the user back to login cleanly rather than leaving the app in a half-authenticated state.

This is one of those cases where security and UX are closely linked. Broken refresh behavior often becomes broken authentication behavior.

Common Pitfalls

  • Using very long-lived access tokens instead of proper refresh flow.
  • Storing long-lived refresh tokens in insecure client-side storage.
  • Treating any 401 as a signal to refresh without checking the actual failure cause.
  • Allowing many concurrent refresh requests to race and overwrite each other.
  • Forgetting logout-time revocation or refresh-token rotation.

Summary

  • JWT expiration is easiest to handle with short-lived access tokens plus refresh tokens.
  • Reject expired access tokens consistently on the server.
  • Keep refresh tokens safer than access tokens and rotate or revoke them when appropriate.
  • Implement a controlled client refresh flow instead of ad hoc retry loops.
  • Treat expiration handling as both a security design problem and a user-session design problem.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.