JSON Web Tokens (JWTs) are everywhere in modern authentication — they show up in Authorization headers, session cookies, and SSO flows. But the moment something goes wrong (a 401 you can't explain, a token that "should" be valid), most developers are stuck staring at a long string of random-looking characters. This guide breaks down exactly what a JWT is, how to read one, and how to debug the most common issues.
The anatomy of a JWT
A JWT is just three Base64URL-encoded JSON objects joined by dots: header.payload.signature. Each part has a distinct role.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
↑ HEADER ↑ PAYLOAD ↑ SIGNATURE- ·Header — specifies the signing algorithm (e.g., HS256, RS256) and token type ("JWT")
- ·Payload — contains the claims: the actual data, such as user ID, roles, and expiration time
- ·Signature — a cryptographic signature over the header and payload, used to verify the token hasn't been tampered with
Decoding the header and payload is just Base64URL decoding — no secret key required, which is why anyone can read the contents of a JWT. The signature is what actually proves authenticity, and verifying it requires the issuer's secret or public key.
Decoding a JWT step by step
Split the token on its dots, then Base64URL-decode the first two segments to get readable JSON.
// Decoded header
{ "alg": "HS256", "typ": "JWT" }
// Decoded payload
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }Note the difference between standard Base64 and Base64URL: JWTs use the URL-safe variant (- and _ instead of + and /, with padding = characters stripped) so the token can be safely placed in URLs and HTTP headers without additional encoding.
JWT Decoder
Paste any JWT to instantly see its decoded header, payload, and expiry status — no copy-pasting into console.log needed.
Base64 ↔ JSON
Manually decode or encode the Base64URL segments of a token to inspect them in detail.
Common claims and what they mean
- ·iss (issuer) — who created and signed the token
- ·sub (subject) — the user or entity the token represents, typically a user ID
- ·aud (audience) — the intended recipient(s) of the token
- ·exp (expiration time) — Unix timestamp after which the token is no longer valid
- ·iat (issued at) — Unix timestamp when the token was created
- ·nbf (not before) — Unix timestamp before which the token must not be accepted
- ·jti (JWT ID) — a unique identifier for the token, often used to prevent replay attacks
Tip: exp and iat are Unix timestamps in seconds (not milliseconds). A common bug is comparing them directly against JavaScript's Date.now(), which returns milliseconds — multiply by 1000 before comparing, or divide Date.now() by 1000.
Common JWT debugging scenarios
"Token expired" errors that seem wrong
Decode the token and check the exp claim against the current time. A frequent cause is a clock skew between the server that issued the token and the server validating it — even a few minutes of drift can cause spurious expiration errors.
"Invalid signature" errors
This means the token's signature doesn't match what the verifying server computes. Common causes: the token was signed with a different secret/key than the one being used to verify it, the algorithm in the header doesn't match what the server expects, or the token was modified (even whitespace) after signing.
Token looks fine when decoded but the API still rejects it
Check the aud and iss claims — many APIs validate that the token was issued by a trusted issuer and intended for their specific service. A token that decodes perfectly but has the wrong audience will still be rejected.
A critical security note
Decoding a JWT does NOT verify it. Anyone can decode a token and read its contents — that requires no secret. Verifying that a token is authentic and untampered requires checking its cryptographic signature with the correct key, which must always happen server-side using a proper JWT library (never trust a client-side decode as proof of authenticity).
Tip: Treat JWTs like passwords in your day-to-day workflow — avoid pasting production tokens into random online tools, and prefer ones (like ours) that decode entirely in your browser without sending the token anywhere.