Skip to content

JWT Decoder

Reads the claims, explains them, and says plainly what decoding does not prove.

Decode a JSON Web Token and read its claims, with expiry shown as a real date. Runs in your tab, so a live session token is never sent anywhere.

Written and maintained by Mohit PatelLast checked August 4, 2026How we build these

A JWT is encoded, not encrypted.

Everything in the payload below is readable by anyone holding the token, including anyone it leaks to. Never put a password, a card number or anything else private in one. Decoding also proves nothing about whether a token is genuine: the signature does that, and checking it needs the signing key, which is not something to paste into a web page.

This page has no server, so the token stays in your tab. That is not true of every decoder, and pasting a production token into one that posts it somewhere is a real way to hand over a live session.

Header
{
  "alg": "HS256",
  "typ": "JWT"
}
Payload
{
  "sub": "1234567890",
  "name": "Ada Lovelace",
  "iat": 1754567000,
  "exp": 1754570600
}
SignaturedozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

Shown as it appears. It is not decoded, because it is not text: it is the raw output of the signing algorithm, and it is meaningless without the key that produced it.

What the claims mean

subSubject: who the token is about, usually a user id.
1234567890
name
Ada Lovelace
iatIssued at.
17545670002025-08-07 11:43:20 UTC — 365 days ago
expExpiry. Seconds since 1970, not milliseconds.
17545706002025-08-07 12:43:20 UTC — 365 days ago — expired

The two readable segments are base64url encoded, which is the same base64 you know with minus and underscore in place of plus and slash and the padding dropped, because a plus becomes a space in a query string. That is the entire difference, and it is why a JWT is full of hyphens and never ends in an equals sign.

Times in a JWT are seconds since 1970, not milliseconds. Passing a JavaScript timestamp straight into an exp claim produces a token that expires in about the year 57000, which is a bug that survives review surprisingly often.

How the JWT Decoder works

Paste a JSON Web Token and see its header and payload decoded, with the standard claims explained and expiry times rendered as real dates so you can tell at a glance whether the token is still valid. It runs entirely in your tab, which matters more here than almost anywhere: a JWT is often a live session, and plenty of online decoders post the token you paste to their own server.

Also known as: decode jwt · json web token decoder · jwt parser online · read jwt payload · jwt token viewer

Encoded is not encrypted

The single most common misunderstanding about JSON Web Tokens is that the payload is protected. It is not. The header and payload are base64url, which is an encoding: a way of writing bytes using characters that survive a URL, reversible by anybody, in one step, with no key. This page does it, your browser's console does it, and so does anyone the token leaks to.

That has a direct consequence for what belongs in one. A JWT payload should carry identifiers and claims, not secrets. Putting an email address in one is usually fine and putting a password, a card number or an internal note about a customer is not. If the contents genuinely need to be hidden from the holder, the format you want is JWE, which does encrypt, and which almost nobody actually needs.

The signature at the end is the only part that is cryptographic, and it protects integrity rather than confidentiality. It proves the token has not been altered since it was issued. It does nothing whatever to stop somebody reading it.

Why decoding proves nothing

A decoder shows you what a token claims. Anyone can produce a token claiming to be an administrator, because producing the header and payload requires nothing but base64. What separates a genuine token from a fabricated one is whether the signature checks out against the key that should have signed it.

Which is why this page decodes and does not verify. Verification needs the signing key, and a key pasted into a web page is a key you have to treat as compromised. Any tool offering to verify your token in the browser is asking for exactly that, and the correct place to verify a token is in the service that holds the key.

So the useful reading of a decoded token is diagnostic rather than authoritative: has it expired, is the audience right, is the issuer who you expected, does the key id point at a key you still have. All questions a decoder can answer honestly.

The none algorithm, and how it broke things

The JWT specification includes an algorithm called none, meaning the token is unsigned. It exists for cases where the token's integrity is guaranteed by some other layer, and it caused one of the most widely exploited authentication vulnerabilities of the last decade.

The flaw was in how libraries used it. A verifier would read the alg field from the header to decide how to check the signature, which sounds reasonable and hands the attacker the decision. Change alg to none, strip the signature, and a library that honoured the header would accept the token as valid. Forging an administrator session took a text editor.

The fix is to decide the expected algorithm on the verifying side and refuse anything else, rather than asking the token what to do with itself. Most libraries now reject none by default. A token declaring it is flagged on this page, because seeing one in the wild means either a deliberate and unusual design or something badly wrong.

Seconds, not milliseconds

JWT timestamps are seconds since the Unix epoch, as RFC 7519 specifies. JavaScript's Date.now returns milliseconds. The two differ by a factor of a thousand and both look like a large number of digits, so passing one where the other is expected produces a token that expires somewhere around the year 57000 rather than in fifteen minutes.

It is a persistent bug because nothing fails. The token is well formed, the signature is valid, every check passes, and the session simply never expires. It typically surfaces months later during a security review, or when somebody notices a token from last year still working.

So expiry and issued-at are rendered here as real dates with a relative distance, and an expired token says so. A claim reading 'in 55000 years' is considerably harder to miss than 1754570600000.

Frequently asked questions

Is a JWT encrypted?

No. The header and payload are base64url encoded, which is a format rather than a secret, so anybody holding the token can read every claim in it. Never put a password, a card number or anything else private in one. If the contents genuinely need to be hidden, you want JWE rather than a plain JWT.

Does decoding tell me the token is genuine?

No, and this is the important limitation. The signature is what proves a token has not been tampered with, and verifying it requires the signing key. Decoding shows you what a token claims; only verification shows you whether to believe it. Do not paste a signing key into a web page to find out.

Is my token sent anywhere?

No. There is no server here to send it to. That is worth checking on any decoder you use, because pasting a production token into one that posts it somewhere is a real way to hand over a live session to a stranger.

Why is the expiry a huge number?

Because JWT times are seconds since 1970, not milliseconds. Passing a JavaScript timestamp straight into an exp claim gives a token that expires around the year 57000, which is a bug that survives code review surprisingly often. Times here are shown as real dates so it is obvious.

What does alg "none" mean?

That the token carries no signature, so anyone can forge one. It exists in the specification for tokens whose integrity is guaranteed some other way, and it has caused real vulnerabilities in libraries that trusted the header to say which algorithm to verify with. A token declaring it is flagged here.

Why is a JWT full of hyphens and underscores?

Because it uses the base64url alphabet, which swaps plus for minus and slash for underscore and drops the padding. A plus sign becomes a space in a query string and a slash breaks a path segment, so ordinary base64 cannot travel in a URL.

Related calculators