What's Actually Inside a JWT: Header, Payload, Signature
A JWT is not encrypted. It is three pieces of Base64URL text joined by dots, and anyone holding the token can read the first two without a key of any kind — which is the single most important thing to understand about it.
Three segments, two dots
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 <- header
.
eyJzdWIiOiIxIiwibmFtZSI6IkFkYSJ9 <- payload
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV… <- signatureEach segment is Base64URL — the ordinary Base64 alphabet with + and / swapped for - and _ so the result is safe in a URL, and the = padding usually dropped.
What each segment holds
| Segment | Holds | Readable without a key? |
|---|---|---|
| Header | How the token is signed — alg, typ, and often kid to name the key. | Yes, by anyone holding the token. |
| Payload | The claims: who the subject is, when it expires, who issued it, plus anything custom. | Yes, by anyone holding the token. |
| Signature | Bytes proving the first two segments were not altered by someone without the key. | Yes, but meaningless without verifying it against the key. |
Encoded is not encrypted
Base64URL is an encoding, not encryption. It exists to make bytes safe in a URL, and reversing it needs nothing but the token. Anything in the payload should be treated as public — a JWT is the wrong place for anything you would not print in a log.
The signature does not hide the contents either. It makes tampering detectable — change one character of the payload and the signature no longer matches — but it does nothing to conceal what is written there.
The registered claims
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who created the token. |
| sub | Subject | Who or what the token is about, often a user id. |
| aud | Audience | Who the token is intended for. |
| exp | Expiration | Seconds since the epoch, after which it should be rejected. |
| nbf | Not before | Seconds since the epoch, before which it should be rejected. |
| iat | Issued at | When it was created. |
| jti | JWT ID | A unique identifier, used to prevent replay. |
These names are reserved so that different systems agree on what they mean. Everything else in the payload is a custom claim — roles, tenant ids, feature flags — and carries no standard meaning beyond what your own code gives it.
Decoding versus verifying
Decoding reads the first two segments. Verification recomputes the signature with the key and compares it. They are separate operations, and only the second establishes that a token is genuine — which is why a decoder alone can never tell you a token is trustworthy.
This is the distinction behind most JWT security advice. Reading a claim to decide what to display is fine; reading a claim to decide what someone is allowed to do is not, unless the signature has been verified first.
Look inside a token
Paste one into the JWT Decoder to see the header and payload as JSON, with the time claims rendered as readable timestamps.
It decodes and does not verify — the signature is never checked, and the output says so on every result. Decoding runs in your browser, which matters here: a real token is a live credential, and this one is not sent anywhere.
Related
For time claims specifically — JWT expired covers exp, Unix time, and clock skew. If a token will not decode at all, why a JWT will not decode covers segment and encoding failures.
Measured with
- DataToolsHQ JWT Decoder —
native decoding (no verification)