JWT Decoder (Decode Header and Payload)
Paste a JSON Web Token to read its header, payload and time claims as plain JSON, with expiry spelled out in human terms. Optionally verify the signature with a shared secret or a public key. Nothing leaves your browser.
Header
Payload
Time claims
| Claim | Value | Reads as |
|---|
This token has no exp, nbf, iat or auth_time claim, so it carries no expiry of its own.
Signature
Signature verification happens in your browser with the Web Crypto API. Decoding alone never proves a token is genuine: anyone can craft a token with any payload they like, and only the signature check can tell you whether it was really signed with your key.
What a JWT actually is
A JSON Web Token is three base64url strings joined by dots. The first is the header, which names the signing algorithm; the second is the payload, a JSON object of claims; the third is the signature. Because the first two parts are only base64url encoded, anyone holding the token can read them with no key at all. That is by design: the payload is where a server puts facts it wants to hand to a client, such as a user id, a role and an expiry time.
eyJhbGciOiJIUzI1NiJ9 decodes to {"alg":"HS256"}. Nothing was decrypted to get there. A decoder is therefore a convenience, not a security boundary, and reading a payload tells you what the token says, never whether it is real.
Decoding is not verifying
This is the mistake that turns a helpful tool into a dangerous one. A signature is computed over the first two segments with a key. Verification means recomputing that value and comparing, which is the only thing that proves two facts at once: the token was produced by somebody holding the key, and neither the header nor the payload has been altered since.
Anyone can invent a token. Take this page's sample payload, change "role":"admin" to anything you like, and the decoded result still looks perfectly well formed while the signature no longer matches. That is why this page shows a "not verified" badge until you supply a key, and why it never describes a decoded token as valid.
The claims worth looking at
- exp - expiry, in seconds since the Unix epoch. Per RFC 7519 the token must be used before this instant, so
now >= expalready means expired. - nbf - not before. The token must not be accepted before this instant, which is how a server publishes a token that only becomes usable later.
- iat - issued at. Useful for spotting a token that was minted long before it was used.
- iss and aud - who issued the token and which service it is meant for. A token with the wrong audience should be rejected even when the signature is perfect.
- sub - the subject, usually a user id. Compare it with what your application expects rather than trusting a display name claim.
All of these times are UTC and none of them carries a time zone, for the same reason Unix timestamps do not: an instant is unambiguous, a wall-clock string is not.
Common JWT mistakes
- Seconds, not milliseconds. An
expbuilt from a JavaScriptDate.now()is a thousand times too large, so the token appears to last until the year 50000. This page flags that case when it sees it. - Trusting
algfrom the token. A server must decide which algorithms it accepts and reject anything else, includingnone. Letting the token choose its own algorithm is the classic JWT vulnerability. - Clock skew. Issuer and verifier are different machines. A minute or two of skew is normal, so most libraries allow a small leeway instead of an exact comparison.
- Putting secrets in the payload. The payload is readable by anyone who has the token, including the user it was issued to. Claims are public data.
- Tokens in URLs. Query strings land in server logs, browser history and referrer headers. Send tokens in an Authorization header.
How to use this decoder
- Paste a token into the box, with or without a leading
Bearer. Decoding starts as you type. - Read the header and payload as formatted JSON, then the time claims table, which spells out each timestamp in UTC, in your local time and as a relative phrase.
- The badge at the top tells you whether the token is still inside its validity window, expired, or not yet valid.
- To check a signature, paste the HMAC secret or a PEM public key and press Verify signature. For HS256 and its siblings the key is the shared secret; for RS, PS and ES you paste the public key only.
- Use Copy on either block. The JSON is plain text, so it pastes cleanly into a bug report or a code comment.
FAQ
Is it safe to paste a real JWT into an online decoder?
This decoder runs entirely inside your browser: the token is never uploaded, and the page makes no network request while decoding. That said, a JWT is a credential, so treat it like a password. Avoid pasting production tokens into sites you do not trust, prefer short-lived tokens when testing, and remember that anything you paste into a browser tab can also be seen by browser extensions you have installed.
What is the difference between decoding and verifying a JWT?
Decoding is just base64url + JSON: anyone can read the header and the payload without any key, because the token is only encoded, not hidden. Verifying recomputes the signature with the key and proves the token was issued by whoever holds that key and was not altered afterwards. A decoded token is not a trusted token, which is why this page never calls a token valid on the strength of a successful decode alone.
Why does my JWT show as expired when it should still be valid?
The usual causes are units and clocks. JWT exp, iat and nbf are in seconds since the epoch, so a value copied from a JavaScript Date.now() is a thousand times too large and lands in the year 50000 or beyond. The second cause is clock skew between the machine that issued the token and the machine reading it, which is worth a minute or two. The third is time zone: exp is always UTC, so a token that expires at 23:30 UTC is already gone at 08:00 the next morning in Tokyo.
Which signature algorithms can this tool verify?
HMAC (HS256, HS384, HS512) with the shared secret, and the public-key families RS256/384/512, PS256/384/512 and ES256/384/512 with the PEM public key. You never need the private key, and you should never paste one here. Tokens with alg set to none have no signature at all: this page reports that instead of showing a green result.
Can a JWT be encrypted?
Yes, but that is a different structure. An encrypted token is a JWE and has five dot-separated parts instead of three, and its payload cannot be read without the decryption key. This decoder handles signed tokens (JWS, three parts); if you paste a five-part token it will tell you so rather than show garbage.