What the three segments are
A JWT is three Base64 URL strings joined by dots:
HEADER.PAYLOAD.SIGNATURE - Header — small JSON object describing the
signing algorithm (
alg) and token type (typ), e.g.{"alg":"HS256","typ":"JWT"}. - Payload — JSON object of "claims" (key/value
pairs). Standard claims like
sub(subject) andexp(expiration) plus whatever the issuer adds. - Signature — binary output of running the algorithm in the header over the first two segments and a key. Used to verify the token wasn't tampered with.
How to decode by hand
Three steps. Take this token:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk - Split on the dot. You get three strings.
- Base64-URL-decode the first two.
eyJhbGciOiJIUzI1NiJ9→{"alg":"HS256"}eyJzdWIiOiIxIn0→{"sub":"1"}
"Base64 URL" — not standard Base64. Replace
-with+and_with/, pad with=to a multiple of 4, then standard-Base64-decode. Why JWTs use Base64 URL → - Don't try to decode the signature. It's binary.
Its only use is being passed back through the algorithm
(
HS256in this case) with the right key to verify.
Common claims you'll see in the payload
| Claim | Name | Meaning |
|---|---|---|
iss | Issuer | Who issued the token (a URL or identifier) |
sub | Subject | Who the token is about — typically a user ID |
aud | Audience | Intended recipient(s); reject if not you |
exp | Expiration | Unix timestamp (seconds); reject if past |
nbf | Not before | Unix timestamp; reject if current time is earlier |
iat | Issued at | Unix timestamp when the token was created |
jti | JWT ID | Unique token identifier — useful for revocation lists |
scope | (OAuth) | Space-separated list of granted scopes |
Issuer-specific claims are added freely. Auth0, Firebase, Cognito, and Okta each have their own conventions on top of the standard ones.
The security warning every JWT guide repeats (because it matters)
Decoding a JWT does not validate it. Anyone can produce a JWT with any claims; the signature is the only thing that proves the issuer actually authored it.
Always verify on the receiver:
- Check the
algin the header matches what you expect — never trust analg: nonetoken. - Verify the signature with the issuer's public key (RS256/ES256) or shared secret (HS256).
- Check
exphasn't passed. - Check
audincludes you andissmatches the expected issuer.
Use a library that does these checks together — don't roll your
own. jose on npm and PyJWT on PyPI are
the modern picks.
Common decode errors
"Invalid token" with a token from your headers
Your Authorization header looks like
Bearer eyJ.... Strip the Bearer prefix
before decoding.
"Unexpected end of input"
The token was cut off — usually a copy-paste that missed the last few characters. JWTs are typically 200–800 characters; short tokens are suspicious.
"Padding error" / "Invalid Base64"
The token was URL-encoded twice, or it's standard Base64 instead of Base64 URL (different alphabet). The decoder above re-pads automatically; if you're decoding manually, see the Base64 vs Base64 URL guide.
Reference
- RFC 7519 — JSON Web Token, the spec.
- RFC 7515 — JSON Web Signature (the signing layer underneath JWT).
- IANA JWT claims registry — every standardized claim and what it means.
FAQ
Is my JWT sent to a server?
No. The decoder runs entirely in your browser. The token never leaves the page — verify with your browser's network tab.
Why do I see 'Invalid token' on a JWT that works in production?
Most likely you copied an extra space, a quote, or part of the surrounding header (e.g. 'Bearer eyJ...' instead of just the token). Strip whitespace and the 'Bearer' prefix.
Does decoding mean my token is valid?
No. Decoding only shows what the token claims. To know whether to trust those claims, the signature must be verified using the issuer's public key (RS256/ES256) or shared secret (HS256). The receiver does this verification, not the decoder.
Can I decode an encrypted (JWE) token here?
No. JWE tokens are encrypted and require a private key to decrypt. This decoder handles JWS (signed but readable) tokens — the 99% case. JWEs have five segments separated by dots; JWS tokens have three.
What's the difference between exp and iat?
Both are Unix timestamps in seconds. 'iat' (issued at) is when the token was created. 'exp' (expiration) is when it stops being valid. A token is expired when the current time is past 'exp', regardless of how long ago 'iat' was.
Is the signature segment readable like header and payload?
No — it's binary, Base64 URL encoded. There's nothing meaningful to read. Its only purpose is to be passed through the verification algorithm together with the header+payload.