JWT Decoder_
Paste a token and it comes apart as you type: the header, payload, and signature split apart color-keyed, every claim lands in a table with its registered meaning, and exp/iat/nbf turn into real dates with an expired, active, or not-yet-valid badge. Bearer prefixes and log-file line wrapping are stripped automatically.
When you need more than a read, verify the signature too — HS256/384/512 against a secret, RS256 or ES256 against a public key pasted as PEM or JWK. Every operation runs inside this tab: the page's security policy forbids its scripts from talking to the network, and the test suite asserts the tool code never tries.
Warnings
| Claim | Value | Meaning |
|---|
Entering a signing secret into any web page is a real risk even when processing stays local — a compromised page could exfiltrate it. Use test or development secrets here, and treat any secret that has touched a browser as due for rotation.
- Input
- A JSON Web Token, with or without a Bearer prefix, and with or without the line wrapping a log file put through it.
- Output
- Header, payload and signature split apart and colour-keyed; every claim in a table with its registered meaning; and exp, iat and nbf turned into real dates with an expired, active or not-yet-valid badge.
- Processing
- Read in this tab. A token IS a live credential, so a decoder that posts it to a server is handing someone a working session — this one cannot, and the no-network policy is enforced by a test rather than promised in a sentence.
- Limits
- Reading a token proves nothing about it. Verification is a separate, deliberate step and needs the key: HS256 with the shared secret, RS256 and ES256 with the public key.
- Not encryption
- The header and payload are JSON made URL-safe, not ciphertext. Anyone holding the token can read every claim in it without a key, which is by design and surprises people constantly.
JWT decoding, from structure to signature
What a JSON Web Token actually contains
A JWT is three chunks of data joined by dots: a header declaring the signing algorithm, a payload of claims, and a signature computed over the first two parts. The header and payload are just JSON made URL-safe — not encrypted, merely re-spelled — which is why reading one needs no secret and no key. Anyone holding a token can read it; that is by design, and it surprises people constantly. Paste a token from a login response, an Authorization header, or a log line, and each segment lands in its own pane with the claims interpreted below.
Is it safe to decode a JWT online?
Only if the site does the work in your browser — a token is a live credential, and a decoder that posts it to a server has just collected something replayable against your API. jwt.io genuinely processes tokens client-side; so does this page; many smaller sites do not, and you can rarely tell. Our guarantee is structural rather than promised: a Content-Security-Policy of connect-src 'self' makes the browser itself refuse any network call this page's scripts might attempt, and an automated test fails the build if any tool function touches fetch. The same caution goes double for the verification box.
Decoding is reading. Verifying is trusting.
Decoding recovers what the token says; verification checks the signature over header.payload with a key and proves who said it — a token can decode perfectly and still be forged. This page keeps the two separate: the panes and claims table appear instantly with no key, while the Verify panel takes a secret (HS256/384/512) or a public key (RS256, ES256) and runs the real cryptographic check via the browser's Web Crypto API. JWKS URL fetching is the one thing we can't offer — the same no-network policy blocks key downloads — so paste the JWK or PEM instead.
Doing it in JavaScript, and the trap in the snippet
The trap in code: atob() speaks standard Base64, token segments use the URL-safe variant, so a raw atob(token.split('.')[1]) breaks on - or _ — the classic snippet swaps those characters back first. The jwt-decode npm package wraps exactly that and nothing more — a jwt decoder library that reads, never verifies. For verification in Node, jsonwebtoken.verify(token, keyOrSecret) checks the signature and time claims in one call; jose and its equivalents cover other stacks. The rule everywhere: decode() in a browser is for display, verify() on the server is for trust.
Paste the token, read the claims, check the expiry
- 01Paste the token — a bare JWT, a full
Authorization: Bearer …header, or a token wrapped across log lines all work; decoding is instant and local. - 02Read the three panes — header, payload, signature — each with its own copy button; Copy_Decoded_JSON grabs header and payload together.
- 03Check the claims table: registered claims come labelled with what validators do, time claims get real dates plus an expired/active/not-yet-valid badge, custom claims pass through untouched.
- 04To verify, enter the secret or the issuer’s public key (SPKI PEM or JWK) and hit Verify — prefer test credentials. Sample loads the RFC 7519 token with its published secret so you can watch a verification succeed.
Four tokens and the question each one answers
Debugging a 401 that used to work
The API rejects a request that worked an hour ago. The exp badge answers it immediately.
exp: 1753860000
2025-07-30T07:20:00Z · Expired 2 hours ago
Inspecting an OIDC id_token
See what your identity provider actually put in the token — audience, issuer, custom claims.
aud: my-app · iss: https://auth.example.com
Audience — who the token is for · Issuer — who created it
Confirming which algorithm your provider signs with
Decode one real token before wiring validation middleware — the header names the algorithm in use.
alg: RS256, kid: 3f9a…
RSA signature — verify with the issuer’s public key, not a secret
Verifying a webhook JWT against a test secret
A vendor signs webhooks with a shared HS256 secret. Paste token and sandbox secret to confirm the scheme.
sandbox secret wh_test_…
✓ Signature verified
The seven registered claims (RFC 7519)
| Claim | Name | Meaning — and what validators check |
|---|---|---|
iss | Issuer | Who created and signed the token; validators compare it to the issuer they trust. |
sub | Subject | Who the token is about — usually the user or account ID your application keys on. |
aud | Audience | Who the token is for, string or array. Validators reject tokens not addressed to them — what stops replay of one service’s token against another. |
exp | Expiration Time | Unix timestamp after which the token must be rejected — the source of the badge in the claims table. |
nbf | Not Before | Timestamp before which the token must be rejected; rare, for tokens issued ahead of validity. |
iat | Issued At | When the token was created — used to judge age or cap lifetime independent of exp. |
jti | JWT ID | Unique ID for this token — the hook for replay detection and revocation lists. |
All seven are optional per the RFC. Everything else is a custom claim: passed through raw, no meanings invented.
Signing algorithms you'll meet in the header
| Algorithm | Key material | Notes |
|---|---|---|
HS256 / HS384 / HS512 | One shared secret (HMAC) | One secret signs and verifies — fine inside one backend, wrong across trust boundaries: whoever can verify can forge. Verifiable here with the secret. |
RS256 | RSA keypair — private signs, public verifies | The OIDC default. Verifiers hold only the public key, so they can check tokens they could never mint. Verifiable here via SPKI PEM or JWK. |
ES256 | ECDSA P-256 keypair | Same trust model as RS256 with 64-byte signatures. Verifiable here — the JWT form is raw r‖s, not DER. |
PS256 / ES384 / ES512 | RSA-PSS / larger ECDSA curves | Decode fully here; verification is outside the supported set, and the panel says so rather than guessing. |
none | No key, no signature | A legal but unsecured JWT. Accepting alg none from untrusted input is a famous vulnerability class — flagged the moment it decodes. |
The header's alg is attacker-writable — validators must pin accepted algorithms, never obey the token.
Habits that stop a token debug going sideways
- Tokens can come straight from a
curl -vdump, a DevTools header, or a log line — Bearer prefixes and wrapping are stripped before decoding. - Read the exp badge first when debugging auth failures: expired tokens cause most 401s, and the relative time points at the broken refresh logic.
- An
audyour service isn’t in explains "valid token, rejected anyway" — validators must refuse tokens addressed to someone else. - For RS256/ES256, match the token header’s
kidto one entry in the issuer’s JWKS document and paste that one JSON object here. - A failing signature against the right-looking secret is often encoding: many providers issue Base64URL-encoded secrets — that’s what the toggle under the key box is for.
What a decoded token does not prove
Decoding is not verification — and payloads are public
Anything readable here is readable by everyone who ever holds the token — a JWT is a signed postcard, not a sealed envelope. Never put passwords, API keys, or needless personal data into claims. A decoded token proves nothing until a signature check passes.
alg none and algorithm-confusion attacks
The header is attacker-controlled. Classic exploits: switching alg to none so naive validators skip the check, or RS256 to HS256 so a server verifies with its own public key as an HMAC secret. Validators must allow-list algorithms; this page warns on both alg none and header/shape disagreements.
A client-side expiry check secures nothing
The badge here is a debugging aid. An attacker controls their own clock and client, so expiry, audience, and signature must be enforced server-side on every request. Client code may check exp only as a courtesy — never instead of server enforcement.
Tokens in localStorage are one XSS from stolen
localStorage is readable by any script on your origin — one injected script exfiltrates every stored token. HttpOnly cookies keep tokens out of JavaScript’s reach; otherwise prefer memory and short lifetimes.
Long-lived tokens have no off switch
A signed JWT stays valid until exp no matter what happens in between — logout, password change, and revocation don’t reach tokens already issued. Short expiries plus refresh tokens, or a jti denylist, are the mitigations; a 30-day access token is an incident waiting to happen.
Pasting production tokens into online decoders — including this one
A live token is a credential; treat any website you paste it into as a potential collector. This page’s safety claim is checkable — CSP blocks its scripts from the network, tests assert no tool code calls out — but take nothing on faith: when stakes are real, decode in your terminal, and rotate any production secret ever typed into a browser.
Algorithms, claims, and where the work happens
- Input tolerance
- Bearer prefix (any case) and whitespace stripped — headers, cookies, log-wrapped tokens paste as-is
- Decoding
- Base64URL per RFC 7515, tolerant of standard-alphabet characters and stray padding; UTF-8 decoded in fatal mode so malformed bytes are reported
- Errors
- Segment-specific: wrong segment count, non-Base64URL header or payload, bytes that aren’t JSON, JSON that isn’t an object
- Warnings
- alg none (unsecured JWT), header/shape disagreements (none with a signature, HS256 with an empty one), missing alg, non-Base64URL signature characters
- Claims
- RFC 7519 registered claims labelled; exp/iat/nbf as ISO UTC plus relative time with expired / active / not-yet-valid badges; aud arrays joined; custom claims verbatim
- Verification
- HS256/384/512 (secret or oct JWK, Base64URL toggle), RS256 and ES256 with SPKI PEM or JWK — all via crypto.subtle in this tab
- Out of scope
- JWKS URL fetching (the no-network policy blocks all requests — paste the key), PS-family and 384/512 asymmetric variants, token generation (a generator is planned)
- Display
- Payloads beyond 300k characters truncate on screen only; Copy always carries the full JSON
Questions about reading and verifying tokens
How do I decode a JWT token?
Split it on the dots, Base64URL-decode the first two segments, and parse the JSON — or paste it above and read the result. No secret is needed; only the signature involves a key.
Is it safe to decode a JWT online?
It depends entirely on whether the token leaves your browser, because a decoder that submits tokens to a server has collected a working credential. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on. Even so, the safest habit for a production token is to decode it in a terminal and trust no website at all, including this one.
Can I decode a JWT without the secret?
Yes — the header and payload are encoded, not encrypted, so reading them requires nothing; the secret matters only for verifying or minting. If the contents must be confidential, plain JWTs are the wrong tool — that is what JWE (encrypted JWTs) exists for.
Why doesn’t my signature verify?
In rough order: the secret is Base64URL-encoded and was pasted raw (flip the toggle), the key doesn’t match the token’s kid, the algorithm needs a public key but got a secret, or the token was tampered with. The messages separate an unusable key from a clean failed check.
What is the exp claim — when does my token expire?
exp is a Unix timestamp (seconds since 1970) after which validators must reject the token; the claims table turns it into a real date with a countdown or an "expired … ago". A token without exp never expires on its own — the table calls that out.
What’s the difference between a JWT and a session cookie?
A session cookie is a random reference the server can kill instantly. A JWT carries its state and verifies statelessly — it scales across services but can’t be revoked early without extra machinery. JWTs suit service auth and federated identity; server-rendered apps often do better with sessions.