Decoding Is Not Verifying
A JSON Web Token looks encrypted and is not. It is three base64url-encoded segments joined by dots, header, payload, signature, and the first two are plainly readable by anyone holding the token. The signature does not hide the contents; it only proves they were not altered after signing.
This decoder splits a token and renders the header and payload as formatted JSON so you can see what a token actually claims.
How to Decode a Token
Paste the whole token including both dots. Decoding happens as you type.
- Paste the token. It should have exactly two dots separating three segments.
- Read the header, which names the signing algorithm in `alg` and often a key id in `kid`.
- Read the payload, where the claims live, `sub`, `iss`, `aud`, `exp`, `iat` and whatever the issuing application added.
- Check `exp` against the current time. It is a Unix timestamp in seconds, not milliseconds, which is a common source of off-by-1000 confusion.
- Note that the signature segment is shown but not checked. Verification needs the secret or public key and belongs on your server.
- Use a test token rather than a live one, for the reason set out below.
What Each Segment Contains
The header is a small JSON object naming the algorithm, commonly `HS256` for a shared secret or `RS256` for a public/private key pair, plus `typ` and sometimes `kid` to tell the verifier which key to use. The payload holds registered claims like `iss`, `sub`, `aud`, `exp`, `nbf` and `iat`, alongside whatever custom claims the application defined.
The signature is computed over the encoded header and payload together. Change a single character of either and the signature no longer matches, which is what makes the token tamper-evident. Note the wording: tamper-evident, not confidential. Anyone who intercepts a token can read every claim in it, which is why tokens should never carry passwords, card numbers or anything else that would matter if seen.
Standard Registered Claims
The claims defined by the JWT specification, and what each is for.
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who issued the token |
| sub | Subject | Who the token is about, usually a user id |
| aud | Audience | Who the token is intended for |
| exp | Expiration | Unix seconds after which it must be rejected |
| nbf | Not before | Unix seconds before which it is not yet valid |
| iat | Issued at | Unix seconds when it was created |
| jti | JWT ID | Unique identifier, used to prevent replay |
All three time claims are Unix seconds, not milliseconds. A token that appears to expire in 1970 or in the year 56000 is almost always a unit mismatch rather than a genuinely broken token.
Why the Signature Is Not Checked Here
Verifying a signature requires the key: the shared secret for HMAC algorithms, or the issuer’s public key for RSA and ECDSA. Pasting a production secret into a web page would be a worse idea than whatever problem it solved, so this tool deliberately decodes only. Trusting an unverified token is the single most common JWT vulnerability, and it belongs on your backend with a library that also checks `exp`, `aud` and the algorithm.
That last point matters more than it sounds. The classic JWT attack is algorithm confusion, an attacker changes `alg` to `none` or swaps RS256 for HS256 so the public key gets used as an HMAC secret. A verifier must pin the expected algorithm rather than trusting what the token declares. Decoding runs entirely in your browser and nothing is transmitted, but a live token in a browser tab is still a live credential, so use an expired or test token where you can.