What’s actually inside a JWT
A JWT is three pieces of Base64url text joined by dots:
header.payload.signature
- Header: the algorithm used to sign, usually
HS256orRS256, and the type. - Payload: the claims (who the token is about, who issued it, when it expires) plus whatever the application adds, like roles or a tenant id.
- Signature: the proof, made over
header.payloadwith a key.
Base64url is not encryption. The first two parts are readable by anyone who has the token, which is why a JWT should never carry a password, a card number or anything you wouldn’t put in a log.
Decoding is not verifying
Reading a token tells you what it claims. It tells you nothing about whether those claims are true.
Anyone can take a token, change "role": "user" to "role": "admin", and re-encode it. What stops that from working is the signature: the server recomputes it with its key and rejects the token when it doesn’t match. So:
- Decoding (what this page does by default) needs no key and proves nothing.
- Verifying needs the key, and is the only thing that makes the claims trustworthy.
Both matter in different moments. When debugging, you usually want to read the claims. When something is being rejected, you want to know whether the signature matches.
The claims you’ll see most
| Claim | Name | What it means |
|---|---|---|
iss |
Issuer | Who created the token |
sub |
Subject | Who or what it’s about, often a user id |
aud |
Audience | Which service is meant to accept it |
exp |
Expires | After this moment the token is dead |
nbf |
Not before | Not valid until this moment |
iat |
Issued at | When it was created |
jti |
Token ID | A unique id, used to revoke or de-duplicate |
The three time claims are counted in seconds since 1970, which is why they look like meaningless numbers in other tools. Here they’re shown as a date plus plain English (“expires in 12 minutes”, “issued 3 days ago”) because that’s usually the thing you’re actually checking.
The alg: none trap
The specification allows a token to declare that it has no signature at all. If a library trusts the header and skips the check, anyone can mint an admin token by hand.
When a token here says alg: "none", the page says so in red. The defence on the server is simple: decide which algorithm you accept before you look at the token, and never let the token choose.
A related trap is algorithm confusion, where an RS256 token is re-signed as HS256 using the public key as the shared secret. The same rule prevents it: pin the expected algorithm.
Checking an HS256 signature here
Enter the shared secret and press Check. Your browser computes the HMAC over header.payload and compares it with the signature, using the Web Crypto API that’s already built in. Three answers are possible:
- Signature matches this secret: the token is intact and was signed with that key.
- Signature does not match: the token was altered, or it was signed with a different key.
- Only HS256, HS384 and HS512 can be checked here: the token uses a public-key algorithm, which needs the issuer’s public key.
None of this leaves the tab. That’s worth caring about: a token pasted into a random website is a credential handed to a stranger, and the same goes for the secret.