JWT decoder
Paste a JSON Web Token below. It is split into its three segments, each is base64url-decoded and parsed, and the header and payload come back as pretty-printed, colour-coded JSON — along with a plain badge telling you whether the token is expired, not yet valid, or currently valid, based on exp and nbf.
Paste a token above, or load the sample, to see it decoded. Nothing you type here is sent anywhere — the decoding happens in this page, in your browser.
Header algorithm & type
Payload claims
Signature not verified
Shown as-is. This tool decodes the header and payload but never checks the signature against a key, so it cannot tell you whether this token is authentic — only its issuer, or whoever holds the matching key, can do that.
Recognised claims
None of this token's keys match a standard registered or common claim name. The full decoded JSON above still shows every field — this table only annotates the ones with a well-known meaning.
| Claim | Where | Value | Meaning |
|---|
How a JWT is put together
A JSON Web Token (RFC 7519) is three parts joined by dots: header.payload.signature. The header and payload are each a small JSON object, base64url-encoded; the signature is a cryptographic value computed over the first two segments using a secret or private key defined by the alg the header names.
Header
Names the signing algorithm (alg, e.g. HS256 or RS256) and usually the token type (typ, almost always "JWT"). It may also carry a kid (key ID) telling the verifier which of several keys to use.
Payload
The claims — statements about a subject. Some are standardised (iss, sub, exp, and so on, listed in full below); most real-world tokens add their own application-specific claims alongside them, and there is nothing wrong with that.
Signature
Computed as HMACSHA256(base64url(header) + "." + base64url(payload), secret) for the common HS256 case, or an equivalent asymmetric signature for RS256/ES256/etc. A verifier recomputes this and checks it matches — that is the entire trust mechanism. This tool has no key, so it shows this segment exactly as received and goes no further.
Base64url, not base64
Each segment is encoded with the URL-safe base64 alphabet: + becomes -, / becomes _, and the trailing = padding is dropped. A decoder has to undo that substitution and restore the padding (or just track bit position without it) before the ordinary base64 algorithm applies. This tool's decoder does that from scratch — no atob, no library — and then runs the resulting bytes through a strict UTF-8 decode, so accented letters, CJK text or emoji inside a claim value come back correct instead of mangled.
Decoding is not verifying. Anyone can read a JWT's claims without any key at all — that is by design; a JWT is signed, not encrypted. What a signature check adds is proof that the claims came from whoever holds the signing key and were not altered afterwards. This page performs the first half only. Treat any claim from a token you haven't verified as an unconfirmed claim, not a fact.
Standard claims reference
RFC 7519 §4.1 registers seven payload claims. None are required by the base spec itself — a profile built on top of JWT (like OpenID Connect or a given API) is what actually mandates which ones must be present.
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who created and signed the token. |
| sub | Subject | The principal the token is about — usually a user or account ID. |
| aud | Audience | The intended recipient(s); a verifier should reject a token meant for someone else. |
| exp | Expiration time | A NumericDate after which the token must be rejected. |
| nbf | Not before | A NumericDate before which the token must not be accepted. |
| iat | Issued at | A NumericDate of when the token was issued. |
| jti | JWT ID | A unique identifier for this token, used to detect replay. |
The header adds its own small set: alg (signing algorithm), typ (usually "JWT"), kid (which key), and occasionally cty (content type, for a nested token). Beyond these, OAuth2 and OpenID Connect define widely-used but non-registered claims such as scope, azp and nonce — this tool recognises those too, in the claims table above, once a token is decoded.
A note on pasting tokens into tools
A production JWT is not a random string — it typically carries a real user ID, sometimes an email address, occasionally role or permission names, and often enough context to identify a specific person or account. Pasting one into a server-side "decode my JWT" tool sends all of that to whoever operates that server, along with your IP address and request headers, for a lookup you could have done offline.
What actually stays true on this page: the decoding — splitting on dots, reversing base64url, parsing JSON, comparing exp/nbf against your device's clock — runs entirely in JavaScript in your browser. The token you paste is not transmitted to any server by this tool. It is still good practice to avoid pasting a live production token into any tool you have not audited, and to rotate a token if you are ever unsure where it has been shared — a decoded JWT reveals its claims to anyone who has the string, verified signature or not.
Use cases & limitations
Where this is genuinely useful
- Debugging an auth flow. Paste the access or ID token your app just received and confirm the claims —
sub,aud,scope— are what you expected, without adding a breakpoint or a log line that might leak it further. - Checking why a request is being rejected as "expired". Decode the token and compare its
expto the current time directly, instead of guessing at clock skew. - Learning the format. The colour-coded output makes it obvious, at a glance, which segment is metadata and which is the actual claim data.
- Reviewing a third-party integration's token shape before you write code against it, so you know which claims are actually present rather than relying on documentation that may be stale.
What this tool cannot tell you
- Whether the token is authentic. That requires the signing key, which this tool never has and never asks for. A token with a completely fabricated payload decodes here exactly as cleanly as a genuine one.
- Whether the token has been revoked. Revocation is normally tracked server-side (a blocklist, a changed key, a session table) and isn't encoded in the token itself.
- Anything about a JWE (encrypted JWT). A 5-segment token is encrypted, not merely encoded, and there is no decode step that reveals its contents without the recipient's private key.
- Whether your server's clock and this page's clock agree. The expiry badge trusts your device's clock. If your server's clock has drifted, its own decision about the same token can differ from what this page reports.
Frequently asked questions
Does this tool verify the signature?
No, and it is not trying to. Verifying a signature means recomputing it with the correct secret or public key and confirming a byte-for-byte match — this page has no key and no way to obtain one. It only reverses the base64url encoding on the header and payload so you can read them, and shows the signature segment as opaque text. Anyone can paste in a token with a header, a payload and a made-up third segment, and this tool will still show you the claims. Decoding tells you what a token claims; only verification with the issuer's key tells you whether that claim is genuine.
Is my token ever sent to a server?
No. The decoding — base64url, JSON parsing, the expiry check — runs in JavaScript in your browser, and what you paste is never transmitted anywhere. That matters more for a JWT than for most tool inputs: production tokens often carry a user ID, an email address, internal role names or a session identifier, and pasting one into a server-side "JWT decoder" hands all of that to whoever runs the server. This page has no server component to send it to.
Why does it say my token is expired or not yet valid?
The payload's exp claim is a NumericDate — seconds since the Unix epoch — marking when the token stops being valid. This tool compares that number against your device's current clock and flags the token Expired the instant exp is in the past. nbf ("not before") works the other way: if it is still in the future, the token is not valid yet even though it exists. Both checks are simple arithmetic, computed the moment you paste the token, using your own clock — if your device's clock is wrong, the verdict will be wrong in exactly the way your clock is.
What is base64url, and why isn't it the same as normal base64?
Standard base64 uses + and / as two of its 64 symbols and pads the output with = signs. Both of those characters mean something in a URL (+ can be read as a space, / separates path segments, = introduces a query parameter), so RFC 7519 has each JWT segment use base64url instead: + becomes -, / becomes _, and trailing = padding is dropped entirely since the segment length already tells a decoder how many bits remain. A decoder has to restore the original alphabet and padding before the ordinary base64 step will work — that substitution is the first thing this tool does with each segment.
Can I decode a token with alg: "none"?
Yes — decoding does not depend on the algorithm at all, it only depends on the token having three dot-separated segments. A token with "alg":"none" typically has an empty third segment, and this tool decodes its header and payload exactly the same way. Worth knowing the other direction too: a server that accepts alg: none as if it were a valid signature is a well-known, serious vulnerability (an attacker can forge any payload with no key at all) — if you are implementing a verifier rather than just reading a token, reject alg: none unless you have a specific, deliberate reason to allow it.
Why are exp, iat and nbf huge numbers instead of dates?
RFC 7519 defines those claims as a NumericDate: an integer count of seconds since 1970-01-01T00:00:00Z UTC, with no timezone, no calendar, nothing to misparse. It is compact, unambiguous, and trivial to compare with a single greater-than check, which is exactly what an exp/nbf comparison needs. This tool converts each one to a UTC date-time string and a relative phrase ("in 2 days", "3 hours ago") next to the raw number, in the recognised-claims table and wherever the value appears.
My token has five parts, not three — is this tool broken?
No — that is a JWE (JSON Web Encryption) rather than the JWS (JSON Web Signature) format this tool reads. A JWS is header.payload.signature, three parts, and its payload is plain base64url — readable by anyone who has the token, which is exactly what this page decodes. A JWE is header.encrypted_key.iv.ciphertext.tag, five parts, and its payload is genuinely encrypted: there is no decoding step that reveals it without the recipient's private key. This tool correctly reports a 5-part input as the wrong segment count rather than guessing at a decode, because there is no key here to actually decrypt it with.