Local JWT decoder, claims inspector and verification checklist
Paste a JWT. I’ll crack it open right here in your browser. You get the header and payload, the expiry and not-before dates spelled out in plain English, plus a quick read on whether the issuer and audience line up with what you expected. Here’s the honest part, though. I decode the token. I don’t verify it. The signature is still yours to check before you trust a single claim, and I’ll get into why that gap matters further down.
All of it runs in your browser, so the token never leaves this page. And to be blunt: I don’t test the signature against any secret, public key or JWKS endpoint. You’re reading what the token claims about itself. That isn’t the same as proof it’s telling the truth.
A JWT decoder is only the first step
Splitting a token into its Base64URL sections is trivial. Trusting what’s inside? Whole other animal, and that’s where people get burned. The header tells you which algorithm and key were meant to be used. The payload hauls around the identity, the issuer and audience, the expiry, scopes and roles, plus whatever custom claims someone bolted on at 2am. None of that is integrity, though. The signature only starts to mean something once your app actually verifies it with the right key and slams the door on the unsafe algorithms.
I built this for the debugging I end up doing constantly. It decodes the header and payload, reads the registered claims, and turns those ugly Unix timestamps into dates a human can actually read. It checks the issuer and audience against what you told it to expect. It flags the expired tokens and the not-valid-yet ones. It shouts when it sees alg: none. And it keeps nagging you that a signature you can see is not a signature anyone has checked. I reach for it staring at API logs, untangling an OAuth handshake, poking at OpenID Connect on my own machine, or chasing some support ticket where I just need to know what the token is really claiming.
How to read a JWT safely
Start with the shape. A normal signed JWT is three sections split by dots: header, payload, signature. Base64URL-decode the first two and out comes JSON. Now the time claims. exp is when the token should stop being accepted. nbf is when it becomes valid. iat is when it was minted. Then check iss and aud against the exact service you were expecting, not a service that’s close enough. Here’s the bit people skip. A token that decodes cleanly but was issued for some other API isn’t your token. Don’t wave it through just because the JSON came out pretty.
- Header tells you the algorithm and token type, plus key-selection hints like
kid. - Payload is where the registered claims live, alongside whatever your app tacked on.
- Claims timeline is how you catch the expired ones. And the ones dated in the future. And the few that are just weirdly old.
- Signature has to be verified on the server, or against a trusted public key, before you lean on it.
- Issues is my shortlist of things to look at twice before a token goes anywhere near code.
Common JWT debugging situations
Getting a 401 back from an API? Decode the token first. Eyeball the audience, the issuer, the expiry and the scope before you start rewriting backend code. I’ve burned hours on a “bug” that turned out to be one expired token, and I’d bet I’m not the only one. Stuck in a frontend login loop? Check whether the browser got handed something already expired, or something with nbf set in the future because two clocks disagree by a few minutes. If the token carries a kid, follow the issuer metadata or a trusted JWKS URL to the matching public key. And the rule I’d never bend: don’t accept a token just because the payload reads right. Anyone can type a believable payload. Forging the signature is the hard part. That’s sort of the whole point of the thing.
Common questions
Does this verify my JWT?
No, and I’m not going to pretend it does. It decodes the token and inspects it, right here in your browser. Actual verification needs the correct secret or public key, a strict allowlist of algorithms, and real issuer plus audience checks. That work belongs in your code. Not on a web page.
Is a JWT encrypted?
Nope. A plain JWT is signed, not encrypted. Anyone holding it reads the header and payload in seconds flat. So whatever you drop in there, assume the whole world can see it. Keep your secrets somewhere else.
What is Base64URL?
The URL-safe cousin of Base64. JWT sections lean on it because a couple of the usual Base64 characters break inside URLs, so those get swapped out and the padding usually gets dropped too.
Does decoding a JWT verify it?
No. Decoding just turns the header and payload back into JSON. Verifying means checking the signature against the secret or public key, and a plain decoder doesn’t touch that step. It’s an easy thing to mix up. Also a painful thing to get wrong in production.
Is it safe to put sensitive data in a JWT?
No. The payload is readable by anyone at all, because it’s signed and not encrypted. So never stash secrets in a JWT. Put in only the stuff you’d be okay seeing leaked, because honestly, it effectively already has.
Why does my JWT show as expired?
Because the exp claim sits in the past now. JWTs are short-lived on purpose. Once one runs out, the client grabs a fresh one with a refresh token, or the user just logs in again.













