JWTs Explained: What's Actually Inside That Token
August 3, 2026
46 reads
If you've ever pasted a JWT into a decoder and been surprised that you could just... read it, that's not a bug. That's the entire design. Here's what's actually going on.
Three parts, separated by dots
A JWT (JSON Web Token) is just three base64url-encoded chunks joined by periods: header.payload.signature. Each part has one job:
- Header — says which algorithm was used to sign the token (usually
HS256orRS256). - Payload — the actual data: who this token represents, what they're allowed to do, when it expires. This is called the set of "claims."
- Signature — a cryptographic proof that the header and payload haven't been tampered with since the server issued them.
Paste any JWT into JWT Decoder and you'll see exactly this: header and payload as readable JSON, signature as an opaque string.
The part that surprises people: it's encoded, not encrypted
Base64 is not encryption — it's just a reversible text encoding, the same way you could describe a number in binary or hex. Anyone who gets hold of a JWT, including a user inspecting their own browser's network tab, can decode the payload and read every claim in it. No secret key required.
This is by design — a JWT is meant to be inspectable, so any service that receives it can read the claims directly instead of calling back to a database. What the signature protects is integrity, not confidentiality: it proves the payload hasn't been altered, but it does nothing to hide what's in it.
The practical consequence: never put a password, API key, or anything actually secret into a JWT payload. If it's sensitive, it doesn't belong in the token — a user ID, a role, or an expiry timestamp is exactly what payloads are for.
What the signature actually stops
The signature is generated using a secret key (for HS256) or a private key (for RS256) that only the issuing server holds. If someone edits the payload — say, changing "role": "user" to "role": "admin" — the signature no longer matches, and any server correctly verifying the token will reject it.
This is why the verification step matters as much as the token itself. A JWT that's never checked against its signature is just an unverified claim someone is making about themselves — the security comes entirely from the receiving server actually validating it before trusting anything inside.
Building and inspecting tokens
- Use JWT Decoder to paste in any token and instantly see its header, payload, and signature broken out — handy for debugging why an API call is being rejected, or checking an
exp(expiry) claim without writing any code. - Use JWT Encoder to build a token with a custom payload and secret — useful for testing an API locally before wiring up real authentication.
Two habits worth keeping
- Always check
exp. Most authentication bugs involving JWTs come down to expired tokens being accepted, or tokens with no expiry at all. Set one, and check it server-side on every request. - Verify signatures server-side, always. Never trust a claim in a payload just because the token "looks valid" — decoding is not the same as verifying, and only signature verification actually confirms the token wasn't forged or altered.