JWT Decoder

Decode JWT headers and payloads, with algorithm and expiry

🔒 Your token is decoded entirely in your browser and never sent to a server.

exp, nbf and iat are checked against this device's clock. If it differs from the server, raise the leeway.

Paste a token above to see the decoded result here.

About JWT Decoder

A JWT (JSON Web Token, RFC 7519) is three base64url strings joined by dots: a header, a payload, and a signature. This tool decodes the first two, parses them as JSON, and turns the exp, iat and nbf claims into readable dates.

It does not verify the signature, and it cannot. Checking an HS256 signature needs the server's shared secret; checking RS256 or ES256 needs the issuer's public key. A decoder takes neither. When the payload shows up cleanly, all you have learned is that the token is well-formed base64url and JSON. Whether anyone should trust it is a separate question that only the verifying server can answer.

Anyone can read a JWT

Base64url is an encoding, not encryption. Whoever gets hold of a token, whether from a log file, a proxy capture, or a browser's localStorage, can read its claims exactly as this page does. Signed tokens (JWS) are therefore the wrong place for passwords, national ID numbers, or internal authorization rules. If the contents must stay secret, the format you want is JWE, an encrypted JWT with five dot-separated parts; paste one here and you will get a segment-count error, because there is nothing readable to decode.

What the signature buys you is integrity. Change "role": "user" to "admin" and re-encode the payload, and the decoder will happily show admin. A correctly written server will reject it, because the signature no longer matches the bytes.

Two attacks that come from trusting the alg header

The alg value sits in the header, which the attacker controls. RFC 8725, the IETF's best-practice document for JWTs, describes two well-known failures that come from letting that value choose the verification method.

  • alg: none. Section 6 of RFC 7519 defines an 'unsecured JWT' whose signature is the empty string. Some libraries saw none in the header and treated the token as valid without checking anything. The second example below has exactly this shape: nothing after the final dot.
  • RS256 to HS256 confusion. The server expects RSA signatures and holds the issuer's public key. The attacker switches alg to HS256 and signs the forged token with HMAC, using that public key, which is public, as the HMAC secret. An implementation that follows the header will compute HMAC with the same public key and accept the forgery.
  • The fix for both: the verifier pins the allowed algorithms in code. RFC 8725 says libraries MUST let the caller specify a supported set of algorithms and MUST NOT use any others, and that each key must be used with exactly one algorithm.
  • Weak HMAC secrets are a third problem. If the HS256 key is a short or memorable string, one captured token is enough to brute-force it offline (RFC 8725, section 2.2).

exp, nbf, iat and clock skew

All three are NumericDate values: seconds since 1970-01-01 UTC, ignoring leap seconds. JavaScript's Date.now() returns milliseconds, so hand-built tokens often end up with an exp tens of thousands of years in the future, or in January 1970. If the date shown here looks absurd, check for a missing or extra factor of 1000.

Per RFC 7519, a token must not be accepted on or after exp, and must not be accepted before nbf. Both sections allow implementers 'some small leeway, usually no more than a few minutes, to account for clock skew.' Without it, a token issued by a server whose clock runs 30 seconds fast can be rejected by the API server as not-yet-valid the moment it arrives. That is a real and confusing bug, because the token looks perfect in any decoder.

The expired and not-yet-valid (before nbf) badges on this page compare exp and nbf with your own computer's clock. If your clock is wrong, so are the badges. Enter a value in the clock skew leeway field to allow that many seconds either way; the default is 0. If iat is in the future, the tool warns that the clocks disagree. iat is only the issue time; it does not make a token expire. A token with no exp claim never expires as far as the standard is concerned, which is why many APIs refuse tokens that lack it.

Things worth checking when a token is rejected

Most 401s with a well-formed token come down to one of a few claims. aud (audience) must match the API; a token minted for one service is often presented to another. iss must match the issuer the API trusts, including the trailing slash some identity providers add. And if kid in the header names a key the server has not fetched yet, for example right after the provider rotated keys, verification fails even though nothing in the token is wrong.

Worked examples

The default token from jwt.io

Input
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Output
Header {"alg":"HS256","typ":"JWT"} Payload {"sub":"1234567890","name":"John Doe","iat":1516239022} Issued 2018-01-18 01:30:22 UTC; no expiry

There is no exp, so there is no expiry verdict. The signature segment is displayed as-is; nothing here tells you whether it is correct.

An alg: none token

Input
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc5MDAwMDAwMH0.
Output
Header {"alg":"none","typ":"JWT"} Payload {"sub":"admin","exp":1790000000} Signature: empty; expires 2026-09-21 14:13:20 UTC

The token ends with a dot and an empty signature. It decodes fine. If a production API accepts tokens like this, anyone can write sub: admin and walk in.

How to use it

  1. Paste the token itself, without the 'Bearer ' prefix.
  2. Read alg and typ in the header and the claims in the payload.
  3. Check exp, iat and nbf as dates; the expiry and nbf badges use your computer's clock, plus any leeway you enter.

Frequently asked questions

Is it safe to paste a production token here?

Decoding happens in your browser and the token is not sent anywhere. A live access token is still a credential, though, so avoid pasting it into chat or bug trackers.

Can this tool tell me if the signature is valid?

No. Verification needs the secret or public key and belongs in your server-side JWT library, configured with an explicit list of allowed algorithms.

It says expired, but the API still accepts it.

The server probably allows a few minutes of leeway, or your computer's clock is ahead. Enter the server's leeway in the clock skew field to judge the token the same way. The reverse case, valid here but rejected by the API, often points to a server clock that is off.

Why do I get a segment-count error?

A JWT has exactly two dots. Check for a leftover 'Bearer ' prefix or quotes, or whether it is an encrypted JWE, which has four.

Is the exp time in my local time zone?

The claim itself is UTC seconds. The tool converts it to a date for display; the underlying instant is the same everywhere.

Sources

Last reviewed:

Privacy and how data is handled