Free Handy Tools

JWT Decoder

This decodes. It does not verify. Reading a token tells you what it claims, not whether those claims are true. Checking the signature needs the key it was signed with — an HMAC secret or the issuer’s public key — which only the issuing service and its verifier hold. A token that decodes cleanly here may still be forged, tampered with, or expired: treat everything below as an unverified assertion.

Reading

Paste a token above and it is taken apart here.

The seven registered claims

  • iss Issuer — Who minted and signed the token.
  • sub Subject — Who or what the token is about — usually a user id.
  • aud Audience — Who the token is meant for. A recipient not named here must reject it.
  • exp Expiration time — The instant after which the token must be refused.
  • nbf Not before — The instant before which the token must be refused.
  • iat Issued at — When the token was created.
  • jti JWT ID — A unique id for this token, so a replay can be spotted.

A token is a live credential for as long as it is valid. This tool splits and base64url-decodes it in your browser, and it never sends the token, never puts it in the address bar and never stores it. Other scripts on this page, such as advertising, are not ours, so a production token is better decoded with a tool on your own machine; an expired or test token is fine here.

Reading a token without trusting it

A JSON Web Token is three pieces of base64url text joined by dots: a header saying how it was signed, a payload of claims, and a signature over the first two. Paste one here and both readable segments are decoded and pretty-printed, every claim is listed, the registered claims are named, and the time claims become dates.

What comes back is a faithful report of what a token says about itself — a different thing from evidence that any of it is true.

Why no green tick appears here

A signature is checked with a key, and this page has none. For the HMAC family — HS256 and its relatives — the key is a shared secret held by the issuer and by whatever accepts the token, and publishing it into a web page would defeat the point of having it. For RS256, ES256 and the rest, the check needs the issuer’s public key, usually fetched from a JWKS endpoint.

So decoding proves nothing. A tampered payload decodes exactly as cleanly as a genuine one: editing the middle segment does not stop it being valid base64url, it only stops the signature matching, and nothing here tests that. Never make an access decision from a decoded token.

One header value makes this concrete. Setting alg to "none" is legal and means the token carries no signature at all. The classic attack was to strip a real signature, set that value, and hand the result to a verifier that took the header’s word for which algorithm to use — which is why any library worth using demands an allow-list from the caller, and why this tool flags the value loudly.

A worked token, claim by claim

Take a header of {"alg":"HS256","typ":"JWT"} and a payload carrying iss, sub, aud, a jti, iat 1767225600 and exp 1767229200. The tool names sub as the Subject and aud as the Audience, and reads those numbers as 1 January 2026 at 00:00 UTC and one hour later — a difference of 3,600 seconds, which is a normal lifetime for an access token.

Those numbers are NumericDate values: seconds since the Unix epoch, not milliseconds. Passing Date.now() straight into a claim lands an expiry in the year 56000; reading seconds as milliseconds dates a 2026 token to 1970. An exp the tool cannot read as a number is shown raw rather than invented into a date.

Claims outside the registered seven are private claims — scope, email, tenant_id, roles — meaning whatever the issuer decided, so the tool labels them as the issuer’s own.

Base64url, and what the payload is not

The segments are base64url, not base64: plus becomes hyphen, slash becomes underscore, and the trailing equals signs are dropped so a token survives being pasted into an address. That is why a decoder insisting on padding rejects perfectly good tokens.

It also means the payload is encoded, not encrypted. Anyone holding the token reads every claim in it, so a JWT is the wrong place for anything you would not put on a postcard. Contents that must stay hidden need JWE, which has five segments rather than three; this tool recognises one and says so.

Decoding happens in JavaScript in your browser, and this tool never sends the token anywhere, which matters because a live token is a session. Other scripts on the page, such as advertising, are not part of the tool, so treat any token pasted into any web page as having had a walk outside.

Encrypted, expired, or simply edited

Is the payload encrypted?

No. It is base64url text, an encoding anyone reverses in a second — this page does it with no key at all. Signing protects a token from being changed, never from being read.

Why does the expiry show as 1970?

Because a NumericDate is seconds since the epoch and something on the way in or out treated it as milliseconds. Date.now() has to be divided by a thousand before it goes into a claim.

Can I change a claim and keep using the token?

Not against anything that verifies properly. Editing the payload invalidates the signature and a correct verifier rejects it. A service that does accept an edited token has a serious bug, rather than you having found a useful trick.

How should a token actually be verified?

On the server, with a maintained library, given an explicit list of acceptable algorithms and the issuer’s key or JWKS URL. Then check iss, aud, exp and nbf against what you expect: a signature proves who wrote the token, not that it was meant for you.

Is it safe to paste a production token here?

Nothing is transmitted from this page, so no copy is kept here. The wider habit is worth resisting: plenty of online decoders do post the token, and a leaked one is a logged-in session for whoever holds it.

What is the difference between exp and nbf?

exp is when the token stops being valid and nbf is when it starts, so a token issued now for use at midnight carries both. Verifiers normally allow a few seconds of leeway on each to absorb clock skew between machines.

How long should an access token live?

Short enough that a stolen one expires before it is useful — minutes rather than days — with a refresh token doing the long-lived work behind a revocation check. A signed token cannot be withdrawn once issued, which is the whole argument for keeping the lifetime brief.