JWT expiry decoder

Paste a token to decode its iat, exp and nbf claims. Fully client-side — your token never leaves this browser.

Why is my JWT expired?

The exp claim is a NumericDate: the number of seconds — not milliseconds — since 1970-01-01T00:00:00Z, as defined by RFC 7519 §4.1.4. Once the verifier's clock passes that instant, the token is rejected, and no amount of re-sending it will help. An expired JWT cannot be extended; it can only be replaced by a freshly issued one.

What iat, exp and nbf actually mean

The exp-in-milliseconds trap

A surprising number of hand-rolled issuers write Date.now() straight into exp. That produces a 13-digit value where the spec wants 10, so the claim reads as a date roughly fifty thousand years in the future and the token effectively never expires. This decoder flags 13-digit claims rather than quietly rendering the year 58,391. If you are not sure which unit a number is in, the digit-count check settles it.

Convert an exp claim to a datetime in your own code

// JavaScript — exp is seconds, Date wants milliseconds
const { exp } = JSON.parse(atob(token.split('.')[1]))
const expiresAt = new Date(exp * 1000)
const expired = Date.now() >= exp * 1000

# Python
import base64, json, time
payload = json.loads(base64.urlsafe_b64decode(token.split('.')[1] + '=='))
expired = time.time() >= payload['exp']

Your token never leaves this page

Decoding a JWT needs no server: the header and payload are base64url-encoded JSON, so the whole job is two string operations and a JSON.parse. This page issues no network request when you paste — open DevTools and watch the Network tab if you would rather verify than trust. That matters because production bearer tokens routinely end up in "just paste it into this online decoder" workflows.

Frequently asked questions

What format is the JWT exp claim?

A NumericDate: the number of seconds — not milliseconds — since 1970-01-01T00:00:00Z, as defined by RFC 7519 section 4.1.4. A valid exp for any current token is therefore ten digits, and rendering it in JavaScript means multiplying by 1000 first, because Date expects milliseconds: new Date(exp * 1000).

What do the iat, exp and nbf claims mean?

iat is "issued at": when the authorization server minted the token. nbf is "not before": the token is invalid until that instant, and most issuers set it equal to iat or a few seconds earlier to absorb clock skew. exp is "expires": the moment the token stops being accepted. A verifier is allowed a small leeway on exp, which is why a token can look just-expired locally and still work against the API for a few more seconds.

Does my token get sent to a server?

No. A JWT's header and payload are base64url-encoded JSON, so decoding is two string operations and a JSON.parse — no server needed. This page issues no network request when you paste; open DevTools and watch the Network tab to verify rather than trust.

Does this decoder verify the token's signature?

No, and deliberately so. Verifying would require your signing key or JWKS endpoint, and a page that asked for your signing key would be indistinguishable from a phishing page. This is a decoder, not a validator — treat the claims as untrusted until your own backend has checked the signature.

Why does my token show as valid here but fail against my API?

Usually clock skew or an nbf in the future. Your machine and the verifier disagree about "now" by a few seconds, and the verifier is stricter. The claims table above shows the raw epoch value, so you can compare it against the server's clock directly.

Can I decode an encrypted token?

No. A JWE has five dot-separated segments instead of three and its payload is ciphertext; no decoder can read it without the decryption key.