Encode Bench › JWT Decoder
JWT Decoder
Paste a token to see its header and payload decoded, with timestamps converted to readable dates. Decoding happens in your browser, which matters because a JWT is a live credential.
● Runs locally. What you paste never leaves this page.
Decoding is not verifying
A JWT has three Base64url segments separated by dots: header, payload and signature. The first two are merely encoded, not encrypted, so anyone holding the token can read every claim in it. That is by design; the signature exists to prove the token has not been altered, not to hide its contents.
This tool decodes. It does not verify the signature, because verification requires the signing key, and pasting a signing key into a web page would be a considerably worse idea than pasting the token. What you get is an honest view of what the token asserts, plus checks on the claims that can be evaluated without a key: expiry, not-before, and whether the algorithm is one that should be refused outright.
Never put a secret in a JWT payload
Because the payload is readable by anyone who has the token, it must not contain anything confidential. Internal identifiers, permission scopes and timestamps are fine. Passwords, API keys, personal data beyond what the client already knows, and anything covered by a data protection obligation are not. If you need the contents hidden, you want an encrypted token, which is a different specification.
The registered claims
| Claim | Meaning |
|---|---|
iss | Issuer, who minted the token |
sub | Subject, usually the user identifier |
aud | Audience, which service should accept it |
exp | Expiry, seconds since the Unix epoch |
nbf | Not valid before this time |
iat | Issued at |
jti | Unique token identifier, used for revocation lists |
The three timestamps are seconds since the epoch, not milliseconds, which is
a frequent source of bugs in JavaScript where Date.now() returns
milliseconds. A token whose expiry appears to be in the year 57000 has been
built with the wrong unit.
Warnings this tool raises
- Expired or not yet valid, judged against your own clock. If a token looks wrongly expired, check that the machine's clock is correct, since clock skew between issuer and verifier is a real cause of intermittent auth failures.
- No expiry claim. A token without
expis valid until the signing key changes, which is rarely what anyone intends. - Algorithm "none". This declares the token unsigned. It exists in the specification and has been the basis of real authentication bypasses, because some libraries historically accepted it. A verifier should reject it unconditionally rather than trusting the header to choose the algorithm.
Why offline matters here
A JWT is usually a live credential. Pasting a production token into a hosted decoder transmits a working key to somebody else's server and, in many environments, is a reportable security event. Everything on this page runs in your browser and no request is made, which you can confirm in the network panel.
Questions
Does this verify the signature?
No. Verification needs the signing key, and that key should never be pasted into a web page. This decodes the header and payload and checks what can be checked without a key, such as expiry and the algorithm.
Is my token sent anywhere?
No. Decoding is Base64url plus JSON parsing, both done in your browser. No network request is made, which matters because a JWT is usually a live credential.
Can I hide data in the payload?
No. The payload is encoded, not encrypted, and anyone with the token can read it. Never put secrets or sensitive personal data in a JWT payload; use an encrypted token if the contents must be hidden.
Why does my expiry date look absurd?
The exp claim is in seconds since the Unix epoch, but JavaScript's Date.now returns milliseconds. Dividing by 1000 when the token is created fixes dates that land thousands of years in the future.
What does an algorithm of none mean?
It declares the token unsigned. Some libraries historically honoured it, which allowed authentication bypass. A verifier should reject none outright rather than reading the algorithm from the token it is meant to be checking.