What is a JWT? How to Decode and Inspect One

Learn what JSON Web Tokens are, how they're structured, and how to decode them to debug authentication issues in your app.

Try it yourself

Use our free JWT Decoder — no sign-up, runs in your browser.

Open tool →

If you’ve ever worked with a modern web app, you’ve almost certainly encountered a JWT. They show up in Authorization headers, cookies, and local storage — but most developers treat them as opaque blobs without really understanding what’s inside.

This post breaks down exactly what a JWT is, how its three parts work, and how to decode one to debug auth issues.

What is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe way to represent claims between two parties. It’s defined by RFC 7519 and is the most widely used token format for authentication and authorization on the web.

When a user logs in, the server creates a JWT containing claims like the user’s ID and role, signs it with a secret or private key, and sends it back. The client stores it and sends it with subsequent requests. The server can verify the signature and trust the claims — without hitting a database.

The three parts of a JWT

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiSmFuZSBEb2UiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.abc123signature

It has three Base64URL-encoded segments separated by dots:

HEADER.PAYLOAD.SIGNATURE

1. Header

The header identifies the token type and the signing algorithm:

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

Common algorithms:

  • HS256 — HMAC with SHA-256 (symmetric, uses a shared secret)
  • RS256 — RSA with SHA-256 (asymmetric, uses a private/public key pair)
  • ES256 — ECDSA with P-256 and SHA-256

Important: alg: "none" is a real algorithm that some libraries support. It means no signature, which is a significant security risk if your library accepts it.

2. Payload

The payload contains claims — statements about the user or the token itself:

{
  "sub": "123456",
  "name": "Jane Doe",
  "role": "admin",
  "iat": 1700000000,
  "exp": 1700003600
}

Registered claims are standardised by the spec:

ClaimMeaning
subSubject — who the token is about
issIssuer — who created the token
audAudience — who the token is intended for
expExpiry — Unix timestamp after which it’s invalid
iatIssued at — when the token was created
nbfNot before — token is invalid before this time
jtiJWT ID — unique identifier for the token

Everything else is a custom claim — your app can put whatever it needs in there.

The payload is not encrypted. It’s only Base64URL-encoded, which anyone can decode. Never put sensitive data like passwords or credit card numbers in a JWT payload.

3. Signature

The signature is what makes JWTs trustworthy. For HS256 it’s computed as:

HMACSHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

If an attacker modifies the payload (e.g. changes "role": "user" to "role": "admin"), the signature won’t match and the server will reject it.

Note that verifying the signature requires the secret or public key — it cannot be done purely in the browser without exposing credentials. Our decoder shows the header and payload only — signature verification requires your server-side secret.

How to decode a JWT

Base64URL decoding is simple. Each part of the JWT is just:

JSON.parse(atob(part.replace(/-/g, '+').replace(/_/g, '/')))

The - and _ characters are Base64URL variants of + and / respectively, and padding (=) is stripped.

Here’s a full decode in JavaScript:

function decodeJwt(token) {
  const [header, payload] = token.split('.').slice(0, 2).map(part => {
    const padded = part.replace(/-/g, '+').replace(/_/g, '/');
    return JSON.parse(atob(padded));
  });
  return { header, payload };
}

Or just use our JWT Decoder — paste the token, see the header and payload decoded instantly.

Common JWT debugging scenarios

Token is being rejected — is it expired? Check the exp claim. It’s a Unix timestamp in seconds. If exp < Date.now() / 1000, it’s expired.

Wrong permissions — check the custom claims Decode the payload and look for whatever your app uses for roles or scopes (e.g. role, permissions, scope).

Wrong audience — check aud If your server validates aud, the token must have been issued for that specific service. A token issued for api.example.com won’t work for admin.example.com.

Signature algorithm mismatch Check alg in the header. If your server expects RS256 but the token uses HS256, it will be rejected.

Security best practices

  • Use short expiry times (exp) for access tokens — 15 minutes to 1 hour is common. Use refresh tokens for long sessions.
  • Never put sensitive data in the payload — it’s readable by anyone who has the token.
  • Always validate the alg header on your server — reject none and only accept the algorithms you expect.
  • Validate aud and iss if your system has multiple services or issuers.
  • Rotate secrets if you suspect a secret has been compromised — this will immediately invalidate all existing tokens.

Ready to try it?

Free, client-side JWT Decoder — nothing sent to a server.

Open JWT Decoder →