Skip to the tool
DevToolBench

JWT Decoder

Read a JWT header, payload and expiry safely.

Decoding happens in this page. Nothing you paste is sent anywhere, and this tool will never ask for your signing key.

header.payload.signature

Everything runs in your browser. Nothing you type is sent to a server.

Found a bug in this tool? Report it.

Share this tool

What this tool does

This JWT decoder splits a token into its three parts, decodes the header and the payload, and explains every claim it finds. Timestamps become readable UTC dates with the distance from your own clock, so you can see at a glance whether a token has expired, when it was issued and how long its lifetime is.

It runs entirely in the page. That matters more here than on most tools: the tokens people need to inspect are usually real ones, taken from a failing request in staging or from a customer's browser, and every claim inside is readable by anyone who holds it. Pasting one into a service that keeps logs is a disclosure. Nothing here is transmitted.

How to use it

  1. Paste the token. A Bearer prefix copied from an Authorization header is fine — it is stripped.
  2. Read the status line: valid, expired, or not yet usable.
  3. Work down the claim list for what each field means.
  4. Copy the payload as JSON if you need it in a bug report.

The three parts

A JWT is three base64url segments joined by full stops: header.payload.signature.

The header says how the token was signed. alg names the algorithm, typ is almost always JWT, and kid tells a verifier which of the issuer's keys to use.

The payload is a JSON object of claims. Seven are registered by RFC 7519 — iss, sub, aud, exp, nbf, iat, jti — and everything else is whatever the issuer decided to put there.

The signature is a MAC or a digital signature over the first two parts. It is the only part that provides any security, and it is the part this page does not touch.

Base64url is used rather than standard Base64 because the token travels in URLs and headers, where + and / are hostile. If you want to see the same encoding in isolation, the Base64 encoder and decoder has a URL-safe mode.

Decoding is not validating

This is the distinction that produces the worst bugs in this area, so it is worth stating plainly.

Decoding reverses an encoding. It needs no key, proves nothing, and anyone can do it — this page does it, your browser's console does it, an attacker who intercepts the token does it. A decoded payload tells you what the token says.

Validating checks the signature against a key you trust, then checks exp, nbf, iss and aud against what you expect. It is the only thing that turns what the token says into something you may act on.

Code that decodes a JWT and trusts a claim from it has no authentication at all. It is a surprisingly common shape: a front end reads sub from the token to decide what to display, which is harmless, and then a back end does the same to decide what to return, which is not.

alg: none, and why the header is not to be trusted

The specification defines none as a valid algorithm for unsecured tokens. Combined with a verifier that reads alg from the header to decide how to check the signature, this produces the classic attack: an attacker rewrites the payload, sets alg to none, drops the signature, and a naive implementation accepts it. A related version swaps RS256 for HS256 so the public key gets used as an HMAC secret.

The lesson is structural. The header is supplied by whoever sent the token, so it cannot be allowed to choose the verification rules. A correct verifier decides in advance which algorithms it accepts and rejects everything else. This page flags alg: none when it sees it, because a token that carries it in the wild is either a test fixture or an attack.

What exp really buys you

exp limits the blast radius of a leaked token and nothing more. It is not revocation: between issue and expiry, a stolen token works. That is why short lifetimes plus refresh tokens are the common pattern, and why a token with no exp at all — which this page will tell you about — is a permanent credential in everything but name.

Two operational details show up constantly here. The claims are seconds since 1970, and a generator that passes milliseconds is out by a factor of a thousand — a 2026 date arrives as the year 57971 — which this page makes obvious rather than subtle. And a token that looks expired by a couple of minutes is usually clock skew between two machines, which is why libraries offer a leeway setting instead of exact comparison.

Frequently asked questions

Does my token leave the browser?

No, and that is the reason this page is built the way it is. The token is split, base64url-decoded and parsed in JavaScript on your machine. There is no request, no logging and no analytics event that carries any part of it. You can paste a live production token here.

Why will this tool not verify the signature?

Because verifying needs the signing key, and a web page that asks for a signing key is teaching people to paste production secrets into a text box. That habit costs far more than the convenience is worth. Verification belongs in the service that holds the key, using a library, on the server.

Is a JWT encrypted?

A signed JWT is not. The header and payload are base64url, which is an encoding, not a cipher — anyone holding the token can read every claim, exactly as this page does. Only a JWE, which has five parts instead of three, encrypts its payload. Never put anything in a JWT you would not put on a postcard.

What does exp actually guarantee?

Only that a compliant verifier should reject the token from that instant onwards. It is a claim inside the token, not a countdown enforced by anything: an expired token stays perfectly readable, and a service that forgets to check exp will keep accepting it forever. It also proves nothing about revocation — a token stolen ten minutes after it was issued is valid for its whole remaining life.

Why does the expiry time here differ from what my server says?

Almost always a units problem or a clock problem. The claims are seconds since 1970, and a token generated with a millisecond timestamp lands thousands of years in the future — this page will show you that immediately. If the difference is a few minutes, it is clock skew between the issuer and the verifier, which is why most libraries allow a small leeway.

Related tools

Updated