JWT Debugger
A 401 with a token that looks fine is almost always one of six things. This page shows you the token the way the server reads it, so you can find out which.
Sign a token of your own to test an endpoint. Everything is computed in this page.
The key and the finished token stay in your browser; neither is sent anywhere, and neither is carried in a share link.
Verify this token in your own code. The snippet follows the algorithm in the header.
Debug a JSON Web Token That Is Being Rejected
Tokens fail in a small number of ways, and none of them are visible in the token itself — it is three chunks of Base64URL with dots between them. Paste it here and it becomes something you can argue with: every header field and every claim, in a table that says what each one is for, with the expiry translated into plain words and the signature checked against whatever key you have.
It decodes as you type, and nothing is sent anywhere. That is worth stating plainly on a page like this, because the thing you are about to paste is a working credential for somebody's account.
The six failures, in the order worth checking
| What went wrong | How it looks here | Fix |
|---|---|---|
| Expired | The line above the header reads "Expired…" | Refresh the token; check the clocks if it is only seconds out |
| Wrong audience | aud names a different API | Request the token for the audience the API expects |
| Wrong issuer | iss is the staging URL, not production | Point the client at the right issuer, or the API at the right one |
| Wrong key | Verify says the signature does not match | Use the key the issuer signed with; watch for Base64 secrets |
| Wrong algorithm | The header says ES256, your key is an RSA key | Match the key to the algorithm shown beside the key box |
| Not a JWS at all | The page names it a JWE with five parts | The payload is encrypted; read it where the decryption key lives |
The order matters. Expiry and audience need no key and no cooperation from anybody, so they cost seconds to rule out; chasing a signature problem that turns out to be an aud mismatch is how an afternoon disappears.
Reading is not verifying
This is the distinction that catches people out. Decoding tells you what a token claims; it says nothing about whether those claims are genuine, because the header and payload are encoded, not encrypted, and anybody can write their own. Verification means recomputing the signature with the issuer's secret or public key — which this page will do in your browser — and it is the only step that turns a claim into a fact.
So use the decoded view to form a theory and the Verify button to test it, and never let an authorisation decision rest on decoded content in your own code. The Code tab prints the server-side check in eight languages for exactly that reason. If the encoding itself is what puzzles you, the difference between Base64 and Base64URL explains why a JWT segment cannot simply be pasted into an ordinary Base64 decoder.
Things that look like token problems and are not
- The word
Bearercopied along with the token, or a trailing newline from a terminal — both change the signing input and break the signature. - A token cut short by a log viewer. The signature segment is a fixed length per algorithm, so a truncated one fails every time.
- Two services sharing a secret where one of them stores it Base64-encoded. Tick Secret is Base64 to reproduce that side.
- A
kidthat no longer exists because the issuer rotated its keys; the token is fine, the key set is stale.
Once the token itself is cleared, the remaining suspects are on the server: a middleware that never validates, a clock that drifts, a cache holding an old key set. If you need a token in a known state to test any of that — expired, unexpired, signed with a key you control — the JWT generator will build one, and the expiry checker explains how a verifier reads the time claims.