JWT Decoder
Decode a JWT in your browser: both JSON parts, what each registered claim means, and whether the token is inside its validity window right now.
# Header
{
"alg": "HS256",
"typ": "JWT"
}
# Payload
{
"sub": "1234567890",
"name": "Ada Lovelace",
"admin": true,
"iat": 1789996400,
"exp": 4102444800
}
# Claims
sub (subject): 1234567890
name: Ada Lovelace
admin: true
iat (issued at): 2026-09-21 13:13:20 UTC
exp (expires at): 2100-01-01 00:00:00 UTC
# Status
Expiry is checked in your browser, against your clock.
Algorithm: HS256. The signature is NOT checked here, and cannot be: that needs the key.
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Paste a JWT and see what is inside it: both JSON parts, what each registered claim means, and whether the token is live right now.
How to use
- Paste the token. A
Bearerprefix and wrapped lines are stripped for you. - Read the header first. The
algvalue is what the issuer says it signed with, and it is the one field an attacker would most like you to trust. - Read the payload. Registered claims are named, and the timestamps are shown as UTC dates with how far away they are.
- Check the status line. It says whether
exphas passed ornbfhas not arrived, using your own clock. - Remember what this cannot do: the signature is not checked. Only the holder of the key can do that.
Example
A token with the usual claims decodes to:
# Header
{
"alg": "HS256",
"typ": "JWT"
}
# Payload
{
"sub": "1234567890",
"name": "Ada Lovelace",
"iat": 1789996400,
"exp": 1790004000
}
# Claims
sub (subject): 1234567890
iat (issued at): 2026-09-21 13:13:20 UTC (1 hour ago)
exp (expires at): 2026-09-21 15:20:00 UTC (in 58 minutes)
The dates are the part worth having. 1790004000 tells you nothing at a glance; “in 58 minutes” tells you whether the bug you are chasing is an expiry.
Pitfalls
- A JWT is signed, not encrypted. Anyone holding it can read every claim, so a token is not a place for anything private.
- Decoding is not verifying. A decoded payload is untrusted input until a service with the key has checked the signature, and no browser tool can do that for you.
alg: noneis a real value, and accepting it is a classic vulnerability. If a token arrives with it, the sender is telling you it is unsigned.- Expiry is checked against your clock. A token that looks expired by seconds may be fine on a server whose clock differs, which is what
leewaysettings exist for. expandiatare seconds, not milliseconds. A token with a year far in the future usually means milliseconds were used by mistake.- Pasting a production token into a website sends it to that website. This tool decodes in your browser and uploads nothing, which is the only version of this that is safe to use with a live token.
- Two dots are required. A two part token is unsecured JWS, and one part is base64 of something else.
- A token with no
expnever expires on its own. That is a design decision worth noticing during a review.
Compatibility
JWT is RFC 7519, and the segments are base64url, which differs from base64 in two characters and has no padding. The payload is decoded as UTF-8, so accented and non-Latin claim values come out correctly. Everything happens in your browser through the standard base64 and text decoding APIs, available in every browser from 2017 onwards.
Frequently asked questions
Can it verify the signature?
Is my token sent anywhere?
Why does the payload look like nonsense?
What does the typ field mean?
JWT. It exists so a receiver can tell JWTs apart from other JOSE objects.Which claims are standard?
iss, sub, aud, exp, nbf, iat and jti. Everything else is yours, and this tool shows it unchanged.