What Is a JWT Token and How Does It Work?

JSON Web Tokens are used in almost every modern web application for authentication. Learn what a JWT contains, how it's verified, and when to use it.

The authentication token behind most modern web apps

If you’ve logged into a web application in the last decade, you’ve almost certainly interacted with a JWT without knowing it. JSON Web Tokens (JWTs, pronounced “jot”) are a standard for securely transmitting information between parties as a compact, URL-safe string. They’re used primarily for authentication — once a user logs in, the server issues a JWT that the client includes in subsequent requests to prove who they are.

Before JWTs, most web apps used server-side sessions: the server stored session data in memory or a database and sent a session ID cookie to the client. JWTs inverted this model — the authentication data lives inside the token itself, and the server doesn’t need to store anything.

Decode and inspect JWT tokens instantly with JWT Decoder.

The three parts of a JWT

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcwMDAwMDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It has three parts separated by dots:

1. Header — Identifies the token type and the signing algorithm:

{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload — Contains the claims (the data being transmitted):

{
  "sub": "user_123",
  "email": "user@example.com",
  "role": "admin",
  "exp": 1700000000
}

3. Signature — A cryptographic signature that verifies the token hasn’t been tampered with.

Each part is Base64url-encoded (a URL-safe variant of Base64). This is why the token looks like random characters — it’s encoded, not encrypted.

Claims: what’s inside the payload

Claims are statements about the user or system. There are registered claims (standardized), public claims, and private claims (custom to your application).

Registered claims:

  • sub — Subject. Usually the user ID.
  • iss — Issuer. Which system issued the token.
  • exp — Expiration time. Unix timestamp after which the token is invalid.
  • iat — Issued at. When the token was created.
  • aud — Audience. Who the token is intended for.

Custom claims — Your application can add anything: role, permissions, teamId, planTier. These are useful for authorization decisions that don’t require a database lookup.

How JWT verification works

When a client sends a JWT with a request, the server:

  1. Splits the token at the dots to get the three parts.
  2. Verifies the signature by re-computing the expected signature using the header, payload, and the secret key (for HMAC algorithms) or public key (for RSA/ECDSA). If the signatures don’t match, the token was tampered with.
  3. Checks expiration — if the current time is past the exp claim, the token is rejected.
  4. Reads the payload — if verification passed, the server trusts the claims and uses them for authorization.

The key insight: the server never stores the token. Everything it needs to know is in the token itself, and the signature proves it’s authentic. This is what makes JWTs “stateless.”

Base64url encoding is not encryption

This is the most important security point: a JWT payload is encoded, not encrypted. Anyone who intercepts a JWT can decode it and read the claims in the payload.

This is fine for authentication data because the goal is verification (proving identity), not secrecy. You wouldn’t put sensitive data like a credit card number or password in a JWT payload.

For sensitive data in tokens, use JWE (JSON Web Encryption) instead of JWT.

Common JWT security mistakes

Storing JWTs in localStorage — LocalStorage is accessible to JavaScript, making the token vulnerable to XSS attacks. HttpOnly cookies are more secure because JavaScript can’t read them.

Not setting expiration — A JWT without an exp claim is valid forever. Always set a reasonable expiration — 15 minutes for access tokens, longer for refresh tokens.

Using “none” algorithm — Some JWT libraries historically accepted "alg": "none" as a valid algorithm (no signature required). Never accept unsigned tokens.

Trusting the header’s algorithm claim — Always verify tokens using a fixed, expected algorithm rather than using whatever algorithm the token’s header specifies. This prevents algorithm substitution attacks.

Not invalidating on logout — Because JWTs are stateless, logging out doesn’t automatically invalidate the token. Common solutions: short expiration with refresh tokens, a token denylist, or rotation on each request.

Debugging JWT issues

When a JWT-authenticated request fails, the most common causes are:

  1. Expired token — Check the exp claim against the current time.
  2. Wrong audience or issuer — The aud or iss claim doesn’t match what the server expects.
  3. Missing required claims — Your application expects a role or permissions claim that isn’t present.
  4. Clock skew — Server and client clocks differ by more than a few seconds, causing the server to see a token as expired that the client thinks is valid.

Decode your JWT with JWT Decoder to inspect the header, payload, and expiration instantly without leaving your browser.


✨ Missing something?
Can't find the tool you need?
Request it — we build new tools based on what people ask for.
Request a tool