JWT Decoder

Decode a JSON Web Token to inspect its header, payload and expiry in your browser — with a clear warning that decoding is not the same as verifying.

🔒 Signature is not verified — decoding only reveals the contents. Never paste production secrets.

Decoding is not verifying

A token is three base64url strings joined by dots: a header, a payload, and a signature over the first two. The header and payload are not encrypted — they are encoded, which is a different word. Anyone can read them, and a decoder is simply the thing that saves you doing it by hand.

What no decoder can do is tell you whether the token is real. Change one field in the payload, re-encode it, and leave the signature exactly as it was:

adminSignatureDecodes?
originalfalseunchangedyes
forgedtruebyte-identicalyes

Both display perfectly here. The signature on the second is now wrong, and finding that out requires the secret the token was signed with — which lives on the server and is the entire point. If you are debugging with a decoder, you are reading a claim, not a fact.

The sharpest version is the header. A token can claim {"alg":"none","typ":"JWT"} and carry an empty third part, and it decodes perfectly well — 3 parts, the last of them 0 characters long. There is nothing wrong with it as a piece of data. Whether it means anything is a question about the server receiving it, and a run of libraries once answered that wrongly.

Two things a decoder has to get right

Base64url is not base64. It swaps + for - and / for _ so the result survives a URL, and drops the = padding. A payload of {"k":"???>>>"} makes all three differences visible at once:

EncodingResult
base64eyJrIjoiPz8/Pj4+In0=
base64urleyJrIjoiPz8_Pj4-In0

Feed the second to a plain base64 decoder and it fails or returns rubbish. This page converts the alphabet back and re-pads to a multiple of four before decoding.

The second is subtler and more often wrong. The browser's atob returns a string of bytes, not text, so decoding a payload with any non-ASCII character and stopping there gives mojibake:

ApproachResult
stopping at the byte string{"name":"José Müller","city":"東京","emoji":"🎸"}
this page{"name":"José Müller","city":"東京","emoji":"🎸"}

Converting the byte string to a byte array and running it through a UTF-8 decoder keeps 3 fields intact, emoji included, and round-trips to exactly what went in. Worth checking on any decoder you rely on: paste a name with an accent in it and see what comes back.

How to use

  1. Paste the token.
  2. Read the decoded header and payload.
  3. Check the expiry and issued-at timestamps.
  4. Remember that anyone holding the token can read it.

Frequently asked questions

Is a JWT encrypted?

No. The standard JWT is signed, not encrypted — the payload is Base64url-encoded and anyone holding the token can read every claim in it. Never put a password, a card number, or anything else confidential in one.

What are the three parts?

Header, payload and signature, separated by dots. The header names the signing algorithm, the payload carries the claims, and the signature proves the first two have not been altered by anyone lacking the key.

Does this tool verify the signature?

No, and the distinction matters. Decoding reads the contents; verifying proves they are authentic, and that requires the secret or public key. A decoder shows what a token says, not whether it is genuine — never make a trust decision on decoded contents alone.

What is the alg none attack?

A vulnerability where a token declares its algorithm as none and a careless library accepts it unsigned, letting anyone forge any claims they like. It was widespread years ago and is largely fixed, but it illustrates the rule: the server must decide which algorithm to accept, never the token.

What do exp and iat mean?

Expiry and issued-at, both Unix timestamps in seconds. There is also nbf, not-before. Expired tokens should be rejected, though clock skew between servers means a small tolerance is usual.

Can a JWT be revoked?

Not easily, which is the format's main weakness. A signed token stays valid until it expires, because verification needs no database lookup — which is precisely the property that makes JWTs scale. Revocation requires a blocklist, which reintroduces the lookup, so short expiry times plus refresh tokens are the usual compromise.

🔒 This tool runs entirely in your browser. Nothing you enter is uploaded, logged, or stored.