Why is my JWT invalid
Tools Nimbus is a free, no-signup developer toolkit that runs entirely in your browser, so your data is never uploaded to a server. A JWT is usually invalid for one of four reasons: it is malformed (not three Base64URL parts, or it still carries a "Bearer" prefix), it is expired, its signature does not verify, or it is not yet valid. Paste it into the Tools Nimbus JWT Decoder, free with no signup, to read the claims and see which one it is.
Last updated July 2026
Decode first, diagnose second
"Invalid JWT" is a symptom, not a cause. A JSON Web Token is three Base64URL-encoded parts joined by dots, in the form header.payload.signature, and it can be rejected at several different stages: your library may fail to even parse it, or it may parse fine and then fail a check on the expiry, the not-before time, or the signature. The header and payload are not encrypted, only encoded, so you can always decode and read them to see exactly what the token claims, even when it is being rejected. That is the fastest way to narrow the problem: decode the token, look at alg in the header and exp, nbf, and iat in the payload, and match the symptom to the table below.
Symptom, cause, and fix at a glance
| Symptom | Most likely cause | Fix |
|---|---|---|
| "Invalid token" before any claim check | Malformed token: not three parts, a Bearer prefix, or stray whitespace | Strip Bearer and whitespace; confirm exactly three dot-separated parts |
| "Token expired" or 401 after a while | exp claim is in the past | Request a fresh token; do not edit exp |
| "Invalid signature" | Wrong key, or the token was altered after signing | Verify with the exact key the issuer used |
| Signature fails only across services | Algorithm mismatch: HS256 secret used for an RS256 token, or vice versa | Match the header alg to the key type |
| "Token not yet valid" | nbf or iat is in the future (clock skew) | Sync clocks (NTP); allow a small leeway window |
| Decodes to garbled text or will not parse | Decoder treats it as standard Base64, not Base64URL | Decode as Base64URL (- and _, no padding) |
Cause 1: the token is malformed
Before any claim is checked, the token has to parse. A valid JWT is exactly three Base64URL segments separated by two dots. The most common way this breaks is a leftover Bearer prefix: the HTTP Authorization header is Bearer <token>, and if you copy the whole header value into a decoder or library, the literal word Bearerand the space are not part of the token. Trailing whitespace, a newline from a copy-paste, or the token being wrapped in quotes cause the same "invalid token" error. Count the dots: two dots mean three parts, which is a signed JWS. Five parts (four dots) is a JWE, an encrypted token that a plain decoder cannot read. Strip the prefix and any whitespace and try again.
Cause 2: the token is expired
This is the single most common reason a token that used to work suddenly returns a 401. The exp claim is a Unix timestamp in seconds (not milliseconds), and once the current time passes it, every correct verifier rejects the token. Decode the payload, read the exp number, and convert it to a readable date to confirm. People often trip here by treating exp as milliseconds and landing tens of thousands of years in the future, which is the same seconds-versus-milliseconds bug behind a Unix timestamp showing 1970. You cannot fix an expired token by editing exp: changing the payload breaks the signature. Ask the issuer for a new token, or use your refresh token to get one.
Cause 3: the signature does not verify
A JWT signature is computed over the header and payload with a key. When verification fails, the verifier recomputed that signature and got a different value than the token carries. Three things cause this. First, the wrong key: for a symmetric HS256 token both sides must share the exact same secret, and a single trailing space or a base64-decoded-versus-raw mismatch in that secret is enough to fail. Second, an algorithm mismatch: if the header says RS256you must verify with the issuer's RSA public key, not an HMAC secret, and confusing the two is a classic bug (and a security pitfall). Third, tampering: if anything changed the header or payload after signing, the signature will not match by design. Note that decoding still works when the signature is invalid, because decoding does not check it, which is exactly why a decoder is a useful first step.
Cause 4: the token is not yet valid
Less common but confusing: a brand-new token is rejected as "not yet valid." The nbf (not before) claim, and sometimes a strict check on iat(issued at), tells the verifier the token should not be accepted before a given time. If the machine that issued the token and the machine verifying it disagree on the clock, a token minted "now" can look like it is from a few seconds in the future. Synchronize both clocks with NTP, and configure a small leeway (a few seconds) in your verification library so minor skew does not reject legitimate tokens.
Cause 5: Base64URL, not Base64
If the token will not decode at all, or decodes to mojibake, the decoder is probably treating it as standard Base64. JWTs use Base64URL, which swaps + for - and / for _ and typically drops the = padding, so a token is URL- and cookie-safe. A standard Base64 decoder can reject the - and _ characters or misread them. This is the same distinction covered in Base64 vs Base64URL. Use a decoder that understands Base64URL, or convert the alphabet back before decoding with a general Base64 tool.
Read the token in one step
You do not have to guess. Open the JWT Decoder and paste the token; it splits the three parts, decodes the header and payload as Base64URL, and shows the claims as readable JSON, including the alg in the header and the time claims in the payload. Check alg against the key you are verifying with, and check exp against the current time (drop the number into the Unix Timestamp Converter to read it as a date). Because the decoder runs entirely in your browser, the token never leaves your device. Note that a decoder reads a token; it does not verify the signature, which needs the secret or public key and belongs on a trusted server.
How to prevent invalid-JWT errors
- Always strip the
Bearerprefix and trim whitespace before handing a token to a library or decoder. - Treat
exp,nbf, andiatas Unix seconds, and refresh tokens before they expire instead of editing claims. - Match the verification key to the header
alg: a shared secret for HS256, the issuer's public key for RS256, and never acceptalg: none. - Keep server clocks in sync with NTP and allow a few seconds of leeway to absorb skew on
nbfandexp. - Decode with a Base64URL-aware tool. For picking one, see the free alternative to jwt.io, and browse the Tools Nimbus guides for more developer fixes.