Paste a JWT and instantly see what's actually inside it — the header and payload, decoded and readable, with every standard claim explained in plain English — entirely in your browser, locally, and offline.
A JWT (JSON Web Token) is three base64url-encoded segments separated by dots:
header.payload.signature. The header and payload are just base64url-encoded JSON — anyone can
decode them without any secret, which is exactly what this tool does. Json Web Viewer's
Decode JWT action splits the token, decodes the header and payload back into readable JSON,
and — deliberately — leaves the signature alone. Decoding is pure local computation — there's no server call
involved at any point, so it works fully offline too, including once the app is installed as a PWA.
That's a real limitation, stated plainly: this tool does not verify the signature. Checking a
signature requires the signing secret or public key, which introduces its own security surface (timing
attacks, algorithm-confusion attacks like a forged "alg":"none") that has no place in a read-only
inspection tool. Use it to read a token while debugging, not to authenticate one.
This is worth stating as plainly as possible, because it's the one fact about this tool that matters most: a token decoding cleanly here proves nothing about whether it's genuine. Decoding a JWT is just reversing base64url encoding — no secret is needed, and nobody would need one to alter a token's payload and re-encode it. The only thing standing between "a JWT that decodes" and "a JWT you should trust" is signature verification against the actual issuer's secret or public key, and this tool deliberately never does that.
Why deliberately, rather than as a missing feature: real signature verification means handling a secret or private key inside a browser tool, and introduces its own attack surface — timing attacks during signature comparison, and algorithm-confusion attacks where a token claims a different (often weaker, or absent) algorithm than the server expects. None of that belongs in what is fundamentally a read-only debugging aid. If you need to know whether a token is authentic, check it against your own backend's real validator — the one that actually holds the signing key — not a browser tool that never sees it.
The clearest illustration of why this distinction matters is a token whose header says
"alg": "none". That's a real, valid JWT — the specification permits an unsigned token — and it
decodes exactly like any other. But it means the token has no signature at all, and anyone
can create or edit one by hand. A server that's misconfigured to accept alg: none will trust a
token that proves nothing. Because this is the single most common real-world JWT vulnerability rather than
a curiosity, the decoder flags it with an explicit unsigned-token warning instead of quietly explaining it
like any other header value.
Every explanation below is either RFC-registered or explicitly labeled as a widely-used convention that isn't — nothing is invented for a field the registry doesn't recognize.
JOSE header parameters (RFC 7515 §4.1):
alg — the cryptographic algorithm used to sign or protect the token; the header can claim anything, and nothing here is verified.typ — the media type of this token, normally "JWT".cty — content type; the media type of the payload, used when the payload is itself a nested JWT.kid — key ID; a hint identifying which key was used to sign the token.jku — JWK Set URL, where the signing key set can be fetched; attacker-controllable, treat as untrusted input.x5u — URL of the X.509 certificate or chain for the signing key; also attacker-controllable.x5t — SHA-1 thumbprint of the X.509 certificate for the signing key.crit — critical; header parameters the recipient must understand and process, or reject the token.Registered payload claims (RFC 7519 §4.1) — all seven:
iss — issuer; the principal that issued the token.sub — subject; the principal the token asserts information about.aud — audience; the recipient(s) this token is intended for. A validator that isn't in this list must reject the token.exp — expiration time; Unix seconds after which the token must not be accepted.nbf — not before; Unix seconds before which the token must not be accepted.iat — issued at; Unix seconds at which the token was issued.jti — JWT ID; a unique identifier for this token, usable to prevent replay.
exp, nbf, and iat also get a relative reading as of the moment you
decoded — "expired ~3 years ago", "expires in ~47 minutes" — so the most common debugging question, is this
token expired, is answered without doing date math by hand.
Conventional claims (OIDC / OAuth 2.0 — widely used, but not RFC 7519-registered):
name — the subject's display name.email — the subject's email address.scope — space-separated OAuth 2.0 scopes granted to this token.roles — roles granted to the subject.azp — authorized party; the client ID this token was issued to.nonce — binds the token to the client session that requested it, preventing replay.sid — session ID; identifies the end-user's session.auth_time — when the end-user last authenticated, in Unix seconds.Anything outside these two lists is shown with its raw value and no explanation. That's a deliberate choice, not a gap the registry will eventually fill: a wrong guess at what a custom claim means is worse than admitting it isn't recognized.
"alg": "none" anywhere in a validator. Covered above in detail
because it's the sharpest version of the first mistake: an unsigned token that a misconfigured server
treats as trusted.
iat "issued in the future" as a fraud signal by itself. It
usually just means clock skew between the server that issued the token and whatever machine is checking
it — worth investigating, but not automatically suspicious on its own.
aud is checked for you. The RFC's definition is clear that a
validator not in a token's audience list must reject it — but that check has to happen in your own
backend's validator. Decoding a token and reading its aud value is not the same as your
service having actually enforced it.
Example — this token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
decodes to this header and payload, with each standard field explained:
{
"header": { "alg": "HS256", "typ": "JWT" },
"payload": { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 },
"claims": { "iat": { "raw": 1516239022, "iso": "2018-01-18T01:30:22.000Z" } },
"explanations": {
"header": { "alg": "the cryptographic algorithm used to sign or protect the token (RFC 7515 §4.1.1) …", … },
"payload": {
"sub": "subject — the principal the token asserts information about (RFC 7519 §4.1.2)",
"iat": "issued at — Unix seconds at which the token was issued (RFC 7519 §4.1.6) — issued ~9 years ago (as of decode)",
…
}
}
}
The explanations come from the RFCs, not guesswork: JOSE header parameters (RFC 7515), the seven registered
claims of RFC 7519 (iss, sub, aud, exp, nbf,
iat, jti), and common OIDC/OAuth claims like azp, nonce,
and scope — labeled as conventional, since they aren't RFC-registered. The three timestamp claims
also get a relative reading as of the moment you decoded ("expired ~3 years ago", "expires in ~47 minutes"),
so the most common debugging question — is this token expired? — is answered without date math. A
token whose header says "alg": "none" gets an explicit unsigned-token warning. Claims the
registry doesn't recognize are listed with their value and no explanation — a wrong explanation is worse than
none.
Open this example in the editor →
The link above pre-loads the token above as plain text. Json Web Viewer auto-detects that it looks like a JWT
and shows a Decode as JWT button right in the toolbar — click it to see the decoded result.
No, deliberately. Verifying a signature needs the signing secret or public key, and brings real security surface (timing attacks, algorithm-confusion) into what is fundamentally a read-only debugging tool. The header and payload are decoded and shown as-is, clearly marked as not verified.
No. Decoding happens entirely in your browser, locally — the token you paste is never sent to a server or stored anywhere outside your own device.
Yes. Decoding is pure local computation with no server call involved at any point, so it works whether or not you're connected — including once the app is installed as a PWA, which precaches this page along with the rest of the app for full offline use.
They are standard JWT timestamp claims stored as Unix seconds: iat is when the token was
issued, nbf is the earliest time it's valid from, and exp is when it expires. The
decoder converts each to a readable ISO 8601 date and adds a relative reading as of the moment you decoded —
e.g. "expired ~3 years ago" or "expires in ~47 minutes" — so you can see at a glance whether an expired
token is why a request is failing.
Yes. Every standard field gets a plain-English explanation sourced from the RFCs: the JOSE header parameters
(alg, typ, kid, and the rest, per RFC 7515), all seven RFC 7519
registered claims (iss, sub, aud, exp, nbf,
iat, jti), and common OIDC/OAuth claims like azp, nonce,
and scope, which are labeled as conventional rather than RFC-registered. A token with
"alg": "none" gets an explicit unsigned-token warning. Claims the registry doesn't recognize
are shown with their value and no explanation — nothing is ever guessed.
Decoding just reverses the base64url encoding to reveal the header and payload as JSON — no secret or key is needed, and anyone can do it, including someone who tampered with the token. Verifying checks the signature against the issuer's secret or public key and proves the token wasn't altered since it was signed. This tool only decodes. A cleanly decoded token tells you nothing about whether it's genuine — that's what a real backend validator, checking against the actual signing key, is for.
Yes, if a server ever accepts it. An alg of none means the token has no
signature at all — anyone can create or edit one by hand, including changing who it claims to represent.
This decoder flags it with an explicit unsigned-token warning rather than explaining it like any other
header value, because it's the single most common real-world JWT vulnerability, not a curiosity.
It means the token's iat (issued at) timestamp is later than the current time on the device
that decoded it — normally a sign of clock skew between the server that issued the token and whatever
machine is checking it, rather than a token from the future. The relative reading calls this out
explicitly ("issued … in the future — check for clock skew") instead of just showing a confusing
negative duration.