JWT 'Invalid Signature' and Other Common Token Errors, Explained

What actually causes a JWT invalid signature error, why it's different from an expired token, and how to read a token's header and payload to find the real problem in seconds.

A JWT is three Base64URL-encoded parts joined by dots — header.payload.signature. The header and payload are just encoded JSON and can be read by anyone without any secret; the signature is the only part that actually proves the token was issued by whoever holds the secret key, and it is where almost every confusing JWT error comes from.

'Invalid signature' almost never means the token is malformed

This error means the server recomputed the signature using its own secret key and got a different result than the one attached to the token. The token's structure and JSON are usually perfectly valid — the mismatch is nearly always caused by one of three things: the secret key used to verify doesn't match the one used to sign (common right after rotating a key or across environments), the algorithm in the header (alg) doesn't match what the server expects, or the payload was tampered with or re-encoded after signing, even by something as small as re-serialising the JSON with different key order or whitespace.

'Token expired' is a completely different, and much simpler, problem

This one has nothing to do with the signature — it means the signature verified correctly, but the 'exp' claim in the payload is a timestamp in the past. It is the token working exactly as designed. The fix is simply to get a new token (usually via a refresh token flow), not to touch the signing logic.

'alg: none' and why it should always be rejected

The JWT spec technically allows an 'alg' value of 'none', meaning the token has no signature at all. A server that trusts the algorithm named inside the token itself — rather than only accepting the algorithm(s) it explicitly expects — can be tricked into accepting a completely unsigned, attacker-crafted token. Always validate against an explicit allow-list of algorithms on the server side, never trust the 'alg' field from the token.

How to actually debug one in under a minute

Paste the token into a decoder and check three things in order: does the header's 'alg' match what your server signs with, is the 'exp' claim in the future, and does the payload contain the claims (like 'sub' or a custom role) that your code expects to read. Nine times out of ten, the bug is in one of those three fields, not in the cryptography itself.

Our JWT decoder splits the header, payload and signature instantly and flags an expired token automatically — since everything runs in your browser, it's safe to paste a real, live token while debugging a production issue.

→ JWT Decoder