JWT Decoder

Decode a JSON Web Token to read its header, payload and expiry. Runs entirely in your browser — the token is never transmitted or logged.

All processing happens in your browser. Nothing is uploaded.

How to use

  1. Paste a JWT into the box. Decoding happens as you type; a Bearer prefix is accepted.
  2. Read the header and payload as formatted JSON.
  3. Check the expiry badge — it resolves exp and nbf against your clock.
  4. The signature is shown as-is. It is not verified; see below for why.

About JSON Web Tokens

A JWT is three base64url-encoded segments joined by dots: a header describing the signing algorithm, a payload of claims, and a signature over the first two. Because the segments are merely encoded, decoding requires no key and no server — which is why this tool can work entirely offline in your browser.

The property that trips people up most often is that a JWT is not encrypted. Base64 is an encoding, not a cipher; anyone who holds the token can read every claim in it. The signature guarantees that the contents were not modified after signing, and nothing more. A user ID or a role in the payload is fine. An API key, an email the user should not see, or anything you would not print in a log does not belong there.

The claims that carry meaning to almost every implementation are the registered ones: exp (expiry), iat (issued at), nbf (not before), sub (subject), iss (issuer) and aud (audience). All three time claims are Unix timestamps in seconds, which is a frequent source of bugs in JavaScript, where Date expects milliseconds.

Finally, a note on trust. This tool tells you what a token says, not whether that token is genuine. Those are different questions, and only the server holding the key can answer the second one. A token that decodes cleanly and has not expired can still be entirely forged.

Frequently asked questions

Is it safe to paste a real token here?

Safer than in most JWT tools, because this one has no server component. The token is decoded by JavaScript in your tab and is never sent anywhere — you can confirm that in your browser's network tab. That said, the habit worth keeping is general: a JWT from a production system is a live credential until it expires, and pasting one into any website is a decision worth making consciously.

Why does this tool not verify the signature?

Because verification needs a key, and asking you for it would be the dangerous part. With an HMAC algorithm the verification key is the same secret used to sign — hand it over and the holder can mint tokens as you. Decoding needs no key at all, so this tool does the part that can be done safely and is explicit about the part it does not do.

Is a JWT encrypted?

No, and this surprises people regularly. The header and payload are base64url-encoded, not encrypted — anyone holding the token can read every claim in it. The signature proves the token was not altered; it does not hide anything. Never put a secret in a JWT payload.

What does alg: none mean?

It declares that the token is unsigned. The field exists in the spec for cases where integrity is guaranteed by other means, but it became a well-known vulnerability: libraries that trusted the header's algorithm field could be handed a token with the signature stripped and alg switched to none, and would accept it. Correct implementations pin the expected algorithm rather than reading it from the token.

Why is exp a plain number instead of a date?

It is a Unix timestamp in seconds, per RFC 7519. The most common bug when working with these in JavaScript is forgetting that Date takes milliseconds — new Date(exp) gives you a date in 1970, while new Date(exp * 1000) is what you meant. This tool shows both the raw number and the resolved local time so the two can be compared.

My token decodes but the site says it is invalid. Why?

Decoding only proves the token is well-formed. A server can reject it for reasons this tool cannot see: a bad signature, a wrong issuer or audience, a revoked session, or clock skew between the two machines. Expiry is the one cause visible from the token alone, which is why it is called out here.