JWT Decoder
Decode and inspect JSON Web Tokens locally, with expiry timeline and signature check.
100% in your browser. Nothing you type ever leaves this page.
Header
Payload
Registered claims, in plain English
Verify signature
Self-test passed: HMAC-SHA256 verified against a known vector on load.
What you are actually looking at
A JWT is three base64url blobs separated by dots: header, payload, signature. The first two are plain JSON the moment you decode them, no key required, which is exactly what this tool does. The signature is the only cryptographic part: it lets the receiving service confirm the token was minted by the expected issuer and has not been edited in transit. If the distinction between encoded and encrypted feels slippery, our article Are JWTs encrypted? settles it with diagrams.
Debugging the three classic failures
Nine times out of ten, a rejected token is one of these. First, expiry: look at the
timeline above; if the marker is past the bar, the token is dead and your refresh logic
did not run. Second, audience or issuer mismatch: the aud and iss
claims must match what the API validates, character for character; a trailing slash in an
issuer URL has burned entire afternoons. Third, signature: wrong secret, wrong key, or an
HS256/RS256 confusion between services. The claim table flags timestamps in human time
precisely so you stop converting epoch seconds in your head at 2 a.m.
The security checks the tool runs for you
The header algorithm gets a badge: red for none (forgeable, reject always),
and a note when the value is unusual. We highlight tokens whose lifetime exceeds 24 hours
because long-lived bearer tokens are a standing invitation: anyone who steals one keeps
access until expiry, and
RFC 8725, the JWT best practices document,
is blunt about keeping lifetimes short. None of this replaces server-side validation; it
just makes problems visible before they reach production.
Keep secrets out of payloads
Internal user IDs, emails, roles: debatable but common. API keys, passwords, personal data you would not print on a postcard: never. Every proxy log, browser history entry and crash report that captures the token captures the payload with it. The rule that survives audits is simple: a JWT payload is public information that happens to be signed.
Frequently asked questions
Is it safe to paste a production JWT here?
Safer than almost anywhere else, because the token never leaves your browser: decoding and signature checks run locally with the Web Crypto API, and you can verify that no network request fires. That said, treat any leaked production token as compromised on principle. If a token grants real access, decode it, learn what you need, then revoke or let it expire.
Why does the tool say my token is readable if it is signed?
Because signing and encrypting are different jobs. The header and payload of a JWS (the common kind of JWT) are just base64url-encoded JSON: anyone who holds the token can read every claim without any key. The signature only proves who issued the token and that nobody altered it. If you need confidentiality, that is JWE, or simply not putting the data in the token.
What is the alg:none attack?
Early JWT libraries accepted tokens whose header declared "alg": "none", meaning no signature at all. An attacker could forge any payload, set alg to none, and pass verification. Modern libraries reject it by default, but the tool flags it in red because the attack still surfaces in legacy code and CTFs. Never accept unsigned tokens in production.
Can this tool verify RS256 tokens?
Yes. Paste the issuer's public key in PEM format (the block starting with BEGIN PUBLIC KEY) and the tool verifies the RSASSA-PKCS1-v1_5 SHA-256 signature locally. Only the public key is involved, so there is nothing secret to leak.
Why is my expired token still accepted by my API?
Most likely clock skew tolerance: many frameworks accept tokens for a grace period (often 30 to 300 seconds) after exp to absorb clock drift between servers. Check your library's leeway setting. If the gap is hours, your server clock is wrong, and that is a different incident.