JWT Decoder

Decode a JSON Web Token in your browser and read its header, claims and expiry in plain words.

Nothing leaves your browser: the token is decoded here, in the page, and never sent anywhere.

Valid now: it expires in 7 years.

The three parts

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzEyMzQ1IiwiYXVkIjoicXVhY2t5YXJkLmNvbSIsImlhdCI6MTczNTY4OTYwMCwiZXhwIjoxOTg3OTc3NjAwLCJyb2xlIjoiYWRtaW4ifQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

headerpayloadsignature

The algorithm is HS256: HMAC with SHA: one shared secret makes the signature and checks it, so anyone who can check this token can also forge it. Decoding only reads what the token says: nothing here checks the signature, so a token shown on this page can still be forged or altered.

header
{
  "alg": "HS256",
  "typ": "JWT"
}
payload
{
  "iss": "https://auth.example.com",
  "sub": "user_12345",
  "aud": "quackyard.com",
  "iat": 1735689600,
  "exp": 1987977600,
  "role": "admin"
}
Claims
ClaimMeansIn this token
issIssuer is who created the token, usually a URL or a domain namehttps://auth.example.com
subSubject is who the token is about, usually a user iduser_12345
audAudience is who the token is meant for, the service that should accept itquackyard.com
iatIssued at is when the token was made17356896002025-01-01T00:00:00.000Z
expExpires is when the token stops being accepted19879776002032-12-30T00:00:00.000Z

Runs in your browser. Nothing you enter is sent anywhere.

How to use it

Paste a token into the JSON Web Token box, or press a sample: Signed token, Expired token or alg: none. It’s decoded as you type, in the page; nothing is sent anywhere. A leading Bearer and any spaces or line breaks are removed first, so an Authorization header value can go in as it is.

  1. The status line checks the token against your device’s clock: valid, expired or not valid yet, and by how much.
  2. The three parts shows the header, payload and signature, each in its own color. Any warnings follow, such as a missing signature.
  3. A sentence names the header’s alg and who holds the key behind it.
  4. header and payload show the decoded JSON.
  5. Claims explains each standard claim the token carries, and shows a time claim’s instant as an ISO 8601 timestamp in UTC.

If the text isn’t a JWT, one sentence names the part that failed and why.

Examples

An access token copied from an Authorization header, checked at 2026-10-14T09:40:00Z:

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzgyMzEiLCJhdWQiOlsiYXBpLmV4YW1wbGUuY29tIiwiYmlsbGluZy5leGFtcGxlLmNvbSJdLCJpYXQiOjE3OTE5Njg0MDAsImV4cCI6MTc5MTk3MjAwMCwic2NvcGUiOiJyZWFkOmludm9pY2VzIn0.1nm_H3GpNBYSDrust9L2urWuNx3Gwn_lXf1-GASBIsg

The status line reads Valid now: it expires in 20 minutes. and Claims shows:

iss  https://auth.example.com
sub  user_8231
aud  api.example.com, billing.example.com
iat  1791968400  2026-10-14T09:00:00.000Z
exp  1791972000  2026-10-14T10:00:00.000Z

The token’s scope is only in the payload JSON: Claims covers iss, sub, aud, iat, nbf and exp.

The same session with its times taken from Date.now(), which counts milliseconds, at the same moment:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzgyMzEiLCJpYXQiOjE3OTE5Njg0MDAwMDAsImV4cCI6MTc5MTk3MjAwMDAwMH0.8qRjoKxJCdR9yqmoudHUZ-Zxv6x2oLOxZVPgwoRzOag
iat  1791968400000  +058755-03-22T00:00:00.000Z
exp  1791972000000  +058755-05-02T16:00:00.000Z

The status line reads Valid now: it expires in 56766 years. JWT times are seconds, so an exp check will pass for the next 56,766 years.

A payload encoded with standard Base64 instead of base64url:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzgyMzEiLCJuYW1lIjoiWm/DqyIsImV4cCI6MTc5MTk3MjAwMH0=.1nm_H3GpNBYSDrust9L2urWuNx3Gwn_lXf1-GASBIsg

The error is The payload isn't base64url: "/" isn't one of its characters (A-Z, a-z, 0-9, "-", "_"). Swap the / for _ and the token decodes, trailing = and all.

Good to know

  • Decoding isn’t verifying. Nothing here checks the signature, and anyone can make a token that decodes to any claims. The payload isn’t encrypted either, so keep secrets out of it.
  • A JWT is three base64url parts (RFC 7519, in the JWS compact form of RFC 7515). Base64url is RFC 4648’s URL-safe alphabet, with - and _ for + and /, and JWTs drop the = padding; this tool accepts it either way.
  • Five parts means an encrypted JWE (RFC 7516), whose payload can’t be read without the key. It fails here with A JWT is three base64url parts separated by dots, and this one has 5.
  • Times are NumericDate: seconds since 1970-01-01T00:00:00Z. An exp written as the string "1767225600" isn’t one and gets the warning The "exp" claim isn't a number of seconds, so its time can't be read.
  • There’s no grace period. Per RFC 7519 a token is expired from the second its exp arrives and not valid before nbf. The RFC lets servers allow a few minutes for clock skew, so a server may accept a token this page just called expired.
  • The status rounds down, with 365-day years: the Expired token sample, 651 days past its exp, reads Expired 1 year ago.
  • HS256 uses one shared secret to sign and verify, so whoever can check the token can forge one. RS, PS, ES and EdDSA sign with a private key and verify with the public one.
  • alg: none means no signature and gets a warning. RFC 8725 (JWT best practices) has verifiers accept only the algorithms they expect, which blocks none and passing an RSA public key off as an HMAC secret.
  • Paste from Bearer on, not the whole Authorization: line, whose colon fails. Tokens are limited to 8,000 characters.

Questions

How do I check if a JWT is expired?

Paste it here: the status line compares exp with your clock. In code, it’s expired once Date.now() >= payload.exp * 1000, since exp is in seconds and Date.now() in milliseconds.

Can I decode a JWT without the secret key?

Yes: the header and payload are only base64url-encoded. The secret, or the public key for RS256 and its kin, is needed only to verify the signature. In Node’s jsonwebtoken, jwt.decode() reads a token and jwt.verify() checks it.

Is it safe to decode a JWT online?

On this page, yes: the token is decoded in your browser and never sent anywhere. An unexpired token works like a password, so don’t paste one into a site that sends it to a server.

How do I decode a JWT in code?

Base64url-decode the middle part and parse the JSON. In Node: JSON.parse(Buffer.from(token.split('.')[1], 'base64url')). Python’s base64.urlsafe_b64decode needs the padding back first: append '=' * (-len(part) % 4).