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.
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:
| Claim | Meaning |
|---|---|
sub | Subject — who the token is about |
iss | Issuer — who created the token |
aud | Audience — who the token is intended for |
exp | Expiry — Unix timestamp after which it’s invalid |
iat | Issued at — when the token was created |
nbf | Not before — token is invalid before this time |
jti | JWT 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
algheader on your server — rejectnoneand only accept the algorithms you expect. - Validate
audandissif your system has multiple services or issuers. - Rotate secrets if you suspect a secret has been compromised — this will immediately invalidate all existing tokens.