JWT decoder
Split a JSON Web Token into its header and payload, read the claims with the timestamps in plain English, and see when it expires. No signature is verified.
Your token
Your token stays in this tab. The work is done by JavaScript in your browser. None of it is uploaded, logged or saved, and the tool keeps working with the network off.
A Bearer prefix copied from an Authorization header is removed for you.
Decoding is not verifying
The first two parts of a JWT are plain JSON that anyone holding the token can read. This tool reads them. It does not check the signature, so it cannot tell you whether the token is genuine. Anyone can write a payload, base64 it and staple any string on the end.
Verify tokens in your own code, on the server, with a library and a key you control.
Payload
Paste a token and its header and claims appear here.
Decoding is not verifying
A JWT is three base64url segments joined by dots. The first two are ordinary JSON, readable by anyone holding the token, including this page. The third is a signature over them.
This tool reads the first two. That tells you what the token claims. It says nothing about whether those claims are true, because anyone can write a JSON object, base64 it, staple any string on the end and call the result a token. A decoded payload saying {"admin": true} has proved precisely nothing.
Verification is a different operation: recomputing the signature with the issuer’s key and comparing. It needs a key this tool does not have and deliberately never asks for, a web page that collects signing keys is a web page that collects the one secret that matters. Verify in your own code, on the server, with a library.
A JWT is not encrypted
Base64 is an encoding, not a cipher. Anything in a JWT payload is visible to whoever holds the token, and to anyone who finds it in a log file, a browser’s storage or a support ticket. Put nothing in there you would not hand over along with it: no passwords, no secrets, no personal data beyond what the holder is entitled to.
The encrypted variant is a JWE, which has five segments instead of three and genuinely cannot be read without a key. If a token here reports five parts, that is what it is.
The alg: none problem
A token can declare that it has no signature. The original specification allowed it, and it has produced real authentication bypasses: an attacker rewrites the payload, sets alg to none, drops the signature, and any library that trusts the header accepts it.
The lesson generalises. The alg header is part of the unverified data. It is a claim like any other. Verification code should decide which algorithm to expect from its own configuration and reject anything else, rather than asking the token what to do with the token.
Expiry, and the millisecond trap
exp, nbf and iat are NumericDates: seconds since the Unix epoch, not milliseconds. Passing Date.now() straight into one makes it a thousand times too large, which is why tokens sometimes appear to expire in the year 56000. That is shown here as it stands rather than being quietly corrected, because it is a real bug in whatever issued the token.
The expiry status compares the claim against your own computer’s clock. A server may allow clock skew, may not check exp at all, or may simply disagree with your machine, so treat it as what the token says about itself.
Questions
Does this check whether the token is valid?
Is a JWT encrypted?
Is my token sent anywhere?
What does alg: none mean?
Why does it say a token has expired when my app still accepts it?
Why is my token's expiry in the year 56000?
It says my token has five parts. What is it?
Can I edit the payload and re-sign it here?
More tools
JSON formatter & validator
Indent it, shrink it, or find out what is wrong with it
Base64 encoder / decoder
Both directions, both alphabets, Unicode included
URL encoder / decoder
Percent-encoding, with the component and whole-URL rules apart