JWT Decoder
Paste a JWT, see the header, payload, signature, algorithm, and expiry breakdown — instantly. Token never leaves this page, so you can paste production JWTs without a second thought.
Why decode a JWT?
- Debugging a 401 from an authenticated API by checking whether the token's exp claim has actually passed.
- Confirming which subject (sub claim) a server-side token belongs to before filing a bug.
- Inspecting custom claims (roles, scopes, tenant IDs) baked in by your auth provider.
- Verifying that the alg header matches what your verifier expects (HS256 vs RS256 mismatches are a common bug).
- Reading the iat / nbf / exp triplet to understand the token's validity window.
- Spot-checking a token from a customer support thread to figure out what permissions they had at the moment of the issue.
How it works
A JWT is three base64url-encoded segments joined by dots: header.payload.signature. We split on the dots, base64url-decode the header and payload (which are JSON), and display each as pretty-printed JSON. The signature is shown as the raw base64url string — we don't verify it (verification needs the secret or public key, which the decoder shouldn't have). When the payload contains iat, exp, or nbf timestamps, we render them as human-readable UTC dates with an 'expired' / 'still valid' label.
Frequently asked questions
Does this verify the signature?
No — and that's deliberate. Signature verification needs the signing key (the HMAC secret for HS algorithms, the public key for RS/ES algorithms). Anything that asks for a key isn't safe to paste production tokens into; this tool is decode-only, which is the safe default for inspection.
Is my token uploaded anywhere?
No. The decode runs as JavaScript on this page — no fetch, no XHR, no analytics on input. Pasting a production JWT here is safe in the same sense as opening one in your local browser dev tools.
What does 'invalid token' mean?
Either the token doesn't have exactly three dot-separated parts (rare — usually means you copied wrong), or the header/payload sections aren't valid base64url-encoded JSON. The first segment must decode to JSON like {"alg":"…", "typ":"JWT"}; the second to a JSON object.
Why are exp/iat shown as dates?
JWT timestamps are 'seconds since epoch' integers per RFC 7519. We multiply by 1000 to get milliseconds, format as ISO UTC, and compare exp to the current time to label the token as expired or valid. Time-skew between your clock and the issuer's matters for borderline cases.
Does it support encrypted JWTs (JWE)?
No — only signed JWTs (JWS, the `header.payload.signature` shape). JWE has five segments and the payload is encrypted, so there's nothing useful to decode without the decryption key. JWE is also rare in practice; almost everything labeled 'JWT' in the wild is JWS.
About this tool
JWT (JSON Web Token, RFC 7519) is the de-facto standard for stateless API authentication. The format is intentionally inspectable: header and payload are just base64url-encoded JSON, so anyone holding a token can read its claims. The signature is what makes the token unforgeable — without the signing key, you can't produce a valid one, but you can still read the contents of any token you have. That distinction is why decoders are everywhere and verifiers are not: decoding needs nothing, verifying needs the key. For debugging, decoding is almost always what you want — checking expiry, scope, subject, or which auth provider issued the token.