▸ Free tool
JWT Decoder.
Paste a JWT to read its header and payload. Decoding happens in your browser — the token never leaves the page.
▸ Decode only
Decoding runs in this tab — nothing is sent anywhere. It does not verify the signature, so a decoded token proves nothing; never trust a JWT you haven't verified server-side. And never paste a production token into this page or any other online decoder — a live token is a password.
▸ Header
— ▸ Payload
— ▸ Claims
Three segments, none of them secret
A JWT is three base64url strings joined by dots: header.payload.signature. The header names the signing algorithm and usually a key id (kid). The payload is a JSON object of claims. The signature covers the
first two segments — it proves nobody edited them, and nothing more.
The part people get wrong: base64url is an encoding, not
encryption. There is no key. Anyone holding the token reads every claim
in it — including this page, which does it with atob and a TextDecoder. A JWT is a
signed postcard: a user id and a role are fine; a password or an API key
are not. If the contents must stay private you want JWE, the encrypted
variant — and almost nothing calling itself "a JWT" is JWE.
The registered claims
RFC 7519 reserves seven claim names; everything else in the payload is yours.
-
iss— Issuer. Who minted it. Pin it, or another tenant's tokens pass. -
sub— Subject. A stable, opaque user id; never a changeable email. -
aud— Audience. Unchecked, a marketing-site token works on your billing API. -
exp— Expiry. Seconds since the Unix epoch, not milliseconds. -
nbf— Not before. Valid only from this time on. -
iat— Issued at. Reject tokens minted before the last password change. -
jti— JWT ID. What a denylist or a replay check keys on.
The panel above renders exp, iat, and nbf as UTC timestamps: NumericDate
integers, so a date tens of thousands of years out means milliseconds crept
in.
Why this decoder is decode-only
Verifying a signature requires the key: the shared secret for HS256, the
issuer's public key for RS256 and ES256 — usually pulled from a JWKS
endpoint and selected by kid. A page
that asked for your signing secret would be a phishing form, so this
tool decodes and stops.
That matters more than it sounds: a decoded token proves nothing. Anyone can hand-craft a payload that reads {"role":"admin"}. Trust starts only after your server checks the signature with a key
it already trusted, then exp, iss, and aud.
Everything here runs locally: no fetch, no analytics, no server to log it. Still: never paste a production
token into this decoder or any other. A live bearer token is a password
until exp, and you cannot audit what
someone else's page does with your keystrokes. Use an expired or
redacted one; if a real one has gone into a website, rotate the key.
alg: none, and algorithm confusion
Two classic breaks, both from trusting the header.
"alg":"none". The JWS
spec defines an unsecured mode with an empty signature. A library that reads
the algorithm out of the token accepts a forged payload signed by nobody —
a bug most major libraries have shipped.
Algorithm confusion. An attacker takes an RS256 token, rewrites
the header to HS256, and signs it
with your RSA public key as the HMAC secret — the public key is public.
A verifier that reads alg from the token
and hands it to a generic verify(token, key) treats that public key as a symmetric secret, and the forgery passes.
The fix for both: the algorithm is a property of the key you hold, not a field in the token. Pin it.
You cannot revoke a JWT — which is when sessions win
Statelessness is the selling point and the bill. Once signed, a token is
valid until exp; there is no
server-side record to delete. Log a user out, ban them, strip an admin
role — the token in their hands keeps verifying.
Two tokens fix it: a short-lived access JWT, five to fifteen minutes, plus a refresh token stored server-side that you can delete. The hot path stays stateless; the refresh exchange is where you re-read the user's real state. A 30-day access token plus "we'll add a denylist later" does not work — that denylist is a database read on every request, which is the session table you were avoiding.
If one backend serves your frontend and owns the database, a session id
in a Secure; HttpOnly; SameSite cookie
wins where it counts: instant revocation, an opaque payload, one indexed read
you already pay for. JWTs earn their keep when the verifier cannot reach that
store — separate services, a third party, an edge worker.
Questions, answered.
Is a JWT encrypted?
No — a standard JWT is signed, not encrypted. The header and payload are base64url, an encoding with no key, so anyone holding the token can read every claim in it, including this page. The signature only proves the token was not altered after it was issued. If the contents genuinely have to stay private you need JWE, the encrypted variant, and almost nothing that calls itself a JWT is JWE.
Does this JWT decoder send my token anywhere?
No. Decoding runs in your tab with atob and TextDecoder, there is no fetch call and no analytics — load the page once, go offline, and it still works. Even so, treat a live token like a password: a bearer token is enough to impersonate its subject until it expires, so use an expired or test token here and in every other online decoder.
How do I verify a JWT signature?
On the server, with the key: the shared secret for HS256, or the issuer's public key for RS256 and ES256 — usually fetched from a JWKS endpoint and selected by the header's kid. Pin the algorithm you expect rather than reading it from the token, then check exp, nbf, iss, and aud. A browser page cannot do any of that honestly, which is why this tool decodes and stops.
What does exp mean, and why is my token expired?
exp is the expiry time as a NumericDate — seconds since the Unix epoch in UTC, not milliseconds. This page renders it as a UTC timestamp and flags it once it is in the past. Two things trip people up: writing the value in milliseconds, which pushes the date tens of thousands of years into the future, and clock skew between issuer and verifier, which is why most libraries allow a leeway of a minute or two.
Can a JWT be revoked?
Not on its own. A signed token stays valid until exp because there is no server-side record to delete — logging a user out, banning them, or downgrading their plan does nothing to a token already in their hands. The usual fix is a short-lived access token of five to fifteen minutes plus a refresh token that is stored server-side and can be deleted. If you need revocation to take effect on the very next request, you need a lookup on every request, which is a session.
Is HS256 or RS256 better for JWTs?
It depends on who verifies. HS256 is HMAC with a shared secret, so every verifier can also mint tokens — fine when one service signs and checks its own. RS256 and ES256 sign with a private key and verify with a public one, so you can hand the public key to any number of services without letting them issue anything. Whichever you pick, pin it in the verifier: the algorithm-confusion attack replays an RS256 token as HS256 using the public key as the HMAC secret, and a verifier that trusts the header's alg field accepts it.
Keep going
Tool
Base64 Encoder
Every JWT segment is base64url. Decode one by hand, or round-trip text and files without the UTF-8 bugs.
Tool
SHA Hash Generator
HS256 is HMAC-SHA-256 — the same hash family that signs most tokens, with a key bolted on.
Article
API vs MCP
Where the bearer token ends up when an agent, not a browser, is the thing calling your API.
Article
REST vs gRPC
Same token, different envelope: an Authorization header over HTTP, metadata over gRPC.
Keep reading
-
▸ Tool
Base64 Encoder / Decoder
The encoding under every JWT segment — base64url, padding and all.
-
▸ Tool
SHA Hash Generator
The signature side: SHA-256 and friends, in the browser.
-
▸ Tool
Epoch Converter
Turn exp, iat and nbf into dates you can reason about.
-
▸ Post
/blog/shopify-liquid-vs-headless/
Where token-based auth shows up in a headless storefront.