Decode JWTs locally with the JWT Decoder - header and payload claims in your browser, nothing uploaded.
Why JWTs break and where to look
JWT-based authentication fails in a handful of recurring ways: expired tokens, clock skew, mismatched issuer or audience, algorithm confusion, and signature verification errors.
Nearly all of them leave a readable trace. A JWT is not opaque - its header and payload are Base64URL-encoded JSON that anyone can decode. The claims tell you what the token claims about itself; your server's error tells you what it rejected.
This guide walks the debugging workflow from the token itself to the library configuration, and shows how to do it without leaking the token to a random website.
JWT anatomy: three segments, one signature
A JWT is three dot-separated segments: header.payload.signature.
The header declares the algorithm (alg) and token type (typ). The payload contains claims - statements about the subject and the token itself. Both are plain JSON, encoded with Base64URL, readable by anyone.
The signature is produced with the algorithm from the header and a key held by the issuer. It lets a verifier confirm the token was not modified and was signed by the expected party.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c# header = {"alg":"HS256","typ":"JWT"}# payload = {"sub":"1234567890","name":"John Doe","iat":1516239022}# signature = HMAC-SHA256(header.payload, secret)The standard claims and what they mean
| Claim | Meaning | When you should care |
|---|---|---|
| exp | Expiration time (Unix seconds) | 401 'token expired' - the most common failure |
| iat | Issued-at time | Comparing with server clock; freshness |
| nbf | Not valid before this time | Tokens rejected immediately after issue |
| iss | Issuer identifier | Mismatched issuer - token from wrong authority |
| aud | Intended audience | Token issued for another service |
| sub | Subject identifier | Which user the token represents |
| jti | Unique token ID | Replay detection and revocation lists |
A safe debugging workflow
- Copy the full JWT from the log, header, or failing request - both dots included.
- Decode it locally with the JWT Decoder - never paste production tokens into an unverified online service.
- Read the payload claims: exp, iat, nbf, iss, aud, sub.
- Convert exp to a human-readable time and compare it with the server's current time, not your laptop's.
- Check iss and aud against what your API is configured to accept.
- If the claims look correct, the failure is in verification: algorithm, key, or clock handling in the library.
- Redact the token before sharing logs with anyone, even internally.
Common failure patterns and fixes
Expired token
The token's exp is in the past. The client must re-authenticate or refresh. If tokens expire too fast, users see constant logins - check the issuer's lifetime setting.
Clock skew
A token rejected as expired or not-yet-valid shortly after issue usually means server and issuer clocks disagree. Fix NTP on servers, and use a small leeway (30-60 seconds) only where the protocol requires it.
Wrong issuer or audience
Decoded claims say one iss/aud, your verifier expects another. Common after moving between environments (dev vs prod) or sharing tokens across services. Align the configuration on both sides.
Signature verification failure
The signature does not match. Causes: wrong key, key rotation mid-flight, mismatched algorithm, or the token was modified in transit. Check that the verifier uses the issuer's current public key.
Algorithm confusion (alg header)
If your verifier trusts the alg header, an attacker can switch RS256 to HS256 and sign with the public key. Verifiers must pin the expected algorithm and reject 'none'. Libraries configured to auto-select are a known vulnerability class.
Decoding is not verifying
Decoding reads the claims - any tool can do it, no key required. Verifying proves the token is authentic: signature, exp, nbf, iss, aud, and algorithm, checked by the server with the correct key.
Debugging usually starts with decoding to understand the token, then moves to verification to understand why the server rejected it.
Be careful with decoder services that claim to verify signatures: real verification requires the signing secret or public key, which a remote service should never ask you to provide. A client-side tool is the safe default.
Debugging tools beyond the decoder
- A client-side JWT decoder for reading claims without uploading the token
- The Timestamp Converter for turning exp/iat Unix seconds into readable dates
- A Base64/Base64URL decoder for inspecting individual segments
- Your language's JWT library with signature verification enabled and the algorithm pinned
- Structured request logging that masks Authorization headers by default
FAQ
Q.Is it safe to decode a JWT online?
A.Only if the tool is client-side and never sends the token anywhere. The decoder on this site runs entirely in your browser. Treat tokens from production as sensitive - they are bearer credentials in many systems.
Q.Why does my token expire immediately after creation?
A.Check iat/nbf against the server clock. If nbf is in the future or the server clock is ahead, a fresh token looks expired or not-yet-valid. Fix NTP and use leeway only where needed.
Q.Do I need the secret to decode a JWT?
A.No. The header and payload are readable without any key. You need the secret only to verify the signature, and you should never share it with a remote service.
Q.Why does my token say alg: none?
A.That header means the token is unsigned - anyone can forge claims. Your server must reject none. If you see it, check your library configuration immediately; alg confusion is a well-known JWT vulnerability.
Q.A token ended up in our logs. Is that a problem?
A.Yes, treat it as compromised. Tokens can be replayed until expiry. Redact or rotate it, stop logging Authorization headers, and add a CI check that fails on token patterns in test output.
Q.exp is seconds; iat is seconds - why does my comparison fail?
A.Both are Unix seconds, but beware milliseconds: if one side multiplies by 1000 and the other does not, values differ by a factor of 1000. Use the timestamp converter to inspect the raw values before debugging anything else.
Q.Why does my JWT decode as 'malformed'?
A.A 'jwt malformed' error usually means the token does not have the expected three dot-separated segments, or one segment is not valid base64url. Common causes: a copied token with a truncated payload, whitespace or line breaks around the token, an extra dot inside a claim value, or an opaque session identifier instead of a real JWT. Paste the raw token into the JWT decoder and check each segment; fix the encoding at the source rather than manually editing the token.
References
- RFC 7519 – JSON Web Token (JWT): https://www.rfc-editor.org/rfc/rfc7519
- RFC 7515 – JSON Web Signature (JWS): https://www.rfc-editor.org/rfc/rfc7515
- RFC 7517 – JSON Web Key (JWK): https://www.rfc-editor.org/rfc/rfc7517
- OWASP JWT Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html
Decode JWTs safely
Read header and payload claims locally in your browser. No upload, no account.
Read the claims, then fix the config
JWT debugging is mostly claim reading: exp, iat, nbf, iss, and aud explain the vast majority of 401s. Signature issues are configuration - algorithm pinning and key management.
Decode tokens locally with the JWT Decoder, convert timestamps with the Timestamp Converter, and keep tokens out of logs and third-party sites.