How to use the JWT decoder
- Paste the token into the box. A leading
Bearerfrom an Authorization header is removed automatically, and so are line breaks. - The header and payload appear as formatted JSON, and the signature is shown as it is.
- Check Claims explained:
exp,iatandnbfare converted to dates in UTC and your local time, with a note such as expired 3 hours ago or valid for 2 days. - For tokens signed with HS256, HS384 or HS512, enter the shared secret and press Verify signature.
Decoding and verification happen in your browser with the built-in Web Crypto API. The token and the secret are not sent anywhere, which matters because a valid token usually grants access to an account.
Worked example
The sample token is:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjQxMDI0NDQ4MDAsInJvbGVzIjpbImFkbWluIl19.WxGV26vGofwBKuDiWW4UuJQzQXN5khtdlPduNGmPINo
Its first part decodes to the header {"alg": "HS256", "typ": "JWT"} and the second part to this payload:
{
"sub": "1234567890",
"name": "Jane Doe",
"iat": 1735689600,
"exp": 4102444800,
"roles": ["admin"]
}
| Claim | Meaning |
|---|---|
iat 1735689600 | Issued 2025-01-01 00:00:00 UTC |
exp 4102444800 | Expires 2100-01-01 00:00:00 UTC |
With the secret my-secret-key the signature verifies as valid; any other secret, or a single changed character in the payload, makes it invalid.
What a JWT contains
A signed JWT (RFC 7519) has three parts separated by dots: header, payload and signature. The first two are JSON encoded with Base64URL, a variant of Base64 that uses - and _ and drops the = padding. You can decode those parts yourself with the Base64 decoder; they are encoded, not encrypted. The signature is computed over header.payload with the algorithm named in alg: HMAC with a shared secret for HS256, or a private key for RS256 and ES256, which then needs the issuer's public key to verify.
The registered claims are iss (issuer), sub (subject), aud (audience), exp (expiration), nbf (not before), iat (issued at) and jti (token ID). Times are NumericDate values: seconds since 1 January 1970 UTC. The Unix timestamp converter explains this format in detail.
Common mistakes
- Trusting a decoded token – anyone can change the payload. A server must verify the signature with the expected algorithm before using any claim.
- Accepting alg none – an unsigned token with
"alg": "none"must always be rejected. The decoder warns you about it. - Storing secrets in the payload – the payload is readable by everyone who holds the token. Never put passwords or private data in it.
- Milliseconds instead of seconds –
expwritten byDate.now()is 1000 times too large and lies thousands of years in the future. - Ignoring clock skew – servers often allow a minute or two of tolerance on
expandnbf, so a token that just expired here may still be accepted for a short time. - Weak HMAC secrets – HS256 secrets should be long and random, at least 32 bytes. Short words can be guessed offline from any captured token; the random string generator creates suitable ones.
- Confusing a JWE with a JWT – a token with five parts is encrypted and cannot be decoded without the key.
Frequently asked questions
Is it safe to paste a real token here?
The token is decoded by JavaScript in your browser and is never sent to a server. Still, treat live tokens like passwords and prefer expired or test tokens when you can.
Does decoding a JWT prove it is valid?
No. Decoding only reads the Base64URL-encoded JSON. Only checking the signature with the correct key shows that the token was issued by the expected party and was not modified.
Can this tool verify RS256 or ES256 tokens?
Not at the moment. Signature checking supports the HMAC algorithms HS256, HS384 and HS512, which use a shared secret. RS256 and ES256 need the issuer's public key.
Why is my token shown as expired?
The exp claim is compared with your device's clock. If the date shown is in the past, the token has expired; if your clock is wrong, the result will be off too.
What does alg none mean?
It means the token carries no signature at all. Anyone can create such a token, so servers must reject it. The decoder shows a clear warning when it sees it.
Why do I get an invalid Base64URL error?
The token was probably cut off or contains characters such as quotes, + or / that do not belong in Base64URL. Copy the complete token again, without quotes.