JWT Decoder

Paste a JSON Web Token to see its header, its claims, and whether it has expired β€” with every timestamp translated into a real date. The token is decoded in your browser and never leaves this device.

Decoded locally. The token is not uploaded, logged or saved β€” not even to this page's address.

Three Parts, Two Dots

A JWT is three base64url-encoded pieces joined by dots: a header that says how the token was signed, a payload of claims, and a signature over the first two. The colours in the decoder above follow that split. Base64url is an encoding, not encryption, so the header and payload are readable by anyone who has the token β€” which is why decoding one needs no key at all.

The signature is the only part that carries any security. It is computed over the exact encoded header and payload, so changing a single character of either invalidates it. That is what stops someone editing their own role from "user" to "admin": they can decode and re-encode the payload freely, but they cannot produce a matching signature without the key.

Decoding Is Not Verifying

This page reads a token; it does not check it. Every claim shown above is exactly what the token says about itself, and a token can say anything. Until a signature has been verified with the issuer's key, the claims are unconfirmed text.

Verification on a real server should do more than check the signature. It should reject any alg it did not expect instead of trusting the header's choice, check exp and nbf against its own clock, confirm that iss is an issuer it trusts, and confirm that aud names this service. A token that is genuinely signed but meant for a different API is still the wrong token.

That is why this tool deliberately has no box for a secret. Pasting a production signing key into a web page β€” any web page β€” is a worse risk than pasting the token, because the key can mint unlimited new tokens. Verify in your own code or test suite, where the key already lives.

Reading the Time Claims

exp, nbf, iat and auth_time are NumericDate values: whole seconds since 1 January 1970 UTC. The decoder converts each into your local time, UTC and a relative phrase, because a ten-digit number is easy to misread and the relative form is usually the question you are asking β€” has this expired, and when?

The most common bug in hand-built tokens is writing milliseconds instead of seconds, since that is what JavaScript's Date.now() returns. The result is a thirteen-digit exp that pushes expiry tens of thousands of years into the future, so the token effectively never expires. The claims table flags any time value large enough to be milliseconds.

Clocks also disagree. A token issued by one server and checked by another running a few seconds behind can look not-yet-valid for a moment, which is why most libraries allow a small leeway, often thirty to sixty seconds. If a token fails an nbf or iat check only occasionally, compare the two servers' clocks before looking anywhere else.

What the Algorithm Tells You

The alg header names how the signature was made, and the family matters more than the digits after it.

alg Key type Worth knowing
HS256 HS384 HS512 Shared secret The issuer and every verifier hold the same secret, so any verifier can also forge tokens. Fine inside one service; a poor fit when several parties need to check tokens.
RS256 RS384 RS512 RSA key pair The most widely supported asymmetric choice. The issuer keeps the private key; verifiers need only the public key, often published as a JWKS.
PS256 PS384 PS512 RSA key pair RSA with the PSS padding scheme, which has a stronger security proof than RS. The same keys as RS, a different signature.
ES256 ES384 ES512 Elliptic-curve key pair Much smaller keys and signatures than RSA for comparable strength.
EdDSA Ed25519 or Ed448 key pair A modern curve with deterministic signatures, so it cannot be weakened by a bad random number generator.
none No key An unsigned token. Legitimate only where the token is already protected some other way, and never something a server should accept from a client.

Mistakes Worth Checking For

  • Trusting the alg header. If a server lets the token choose its own algorithm, an attacker can send alg "none", or send HS256 signed with the server's RSA public key as the HMAC secret. Pin the algorithm on the verifying side.
  • Secrets in the payload. The payload is readable by anyone holding the token, including the browser it was issued to. Passwords, API keys and personal data the client should not see do not belong in it.
  • Tokens that never expire. Without exp, a leaked token stays valid until the signing key is rotated. Short lifetimes plus refresh tokens limit how long a leak matters.
  • Skipping the audience check. A token issued for one of your APIs will verify perfectly against another that shares the same key. Checking aud is what keeps them apart.

Why Decode Locally

A JWT is a bearer credential: whoever holds an unexpired one can usually act as the person it was issued to. Pasting a live token into an online decoder hands that credential to a site you know nothing about.

This page decodes the token with JavaScript in your browser. It makes no network request with it, does not store it, and does not put it in the address bar, where it would end up in your history and in server logs. You can confirm that by opening your browser's network panel before pasting. Even so, the safest token to paste anywhere is one that has already expired.

Related Tools

Each part of a JWT is base64url, a variant of Base64 that swaps + and / for - and _ and usually drops the padding. Decode a single segment with the Base64 Encoder.

A claims set is just a JSON object, and building one by hand for a test token is easier with validation. Check it with the JSON Formatter.

HS256 is HMAC over SHA-256, and understanding the digest underneath helps when a signature will not match. Compare SHA digests with the Hash Generator.

The jti claim needs a value that is unique per token, which is exactly what a UUID is. Generate one with the UUID Generator.

References

Frequently Asked Questions

Is it safe to paste a JWT into this decoder?

The token is decoded by JavaScript in your browser and is never sent to a server, stored, or written into the page address, so this site does not receive it. It is still a live credential, though: anyone who sees it while it is valid can usually use it. Prefer tokens from test environments, or ones that have already expired.

Does decoding a JWT verify it?

No. Decoding only reads the header and payload, which are base64url-encoded rather than encrypted, so it needs no key. Verification checks the signature with the issuer's secret or public key and then checks the claims. This page only decodes, so treat what it shows as what the token claims, not as proof.

Why can anyone read my JWT's payload?

Because a signed JWT is encoded, not encrypted. The signature proves the payload has not been altered, but it does nothing to hide it. If the contents must be confidential, use an encrypted JWT (JWE), or keep the data on the server and put only an identifier in the token.

What do exp, iat and nbf mean?

They are timestamps in seconds since 1 January 1970 UTC. exp is when the token expires, nbf is when it becomes valid, and iat is when it was issued. The decoder shows each as a local date, a UTC date and a relative time, and states whether the token is currently expired.

Why does my token say it expires thousands of years from now?

Its exp was almost certainly written in milliseconds rather than seconds, usually by passing JavaScript's Date.now() straight in. The standard requires seconds, so a millisecond value is read as a date tens of thousands of years away. Divide by 1000 when creating the token.

Can this tool decode an encrypted JWT (JWE)?

It can read the header, which is always plain, but not the payload. An encrypted JWT has five parts instead of three, and its payload can only be decrypted with the recipient's key. The decoder recognises the five-part form and tells you which encryption method the header names.