Anatomy of a JWT
A JSON Web Token is three Base64URL strings joined by dots: header.payload.signature. The header names the signing algorithm (alg), the type (typ) and often a key ID (kid) that tells the receiver which key to fetch from a JWKS endpoint. The payload carries claims: registered ones such as iss (issuer), sub (subject), aud (audience), exp (expiry), nbf (not before), iat (issued at) and jti (token ID), plus whatever the identity provider adds, like roles or scopes. The signature covers the first two segments.
The important point is that the header and payload are encoded, not encrypted. Anyone holding the token can read them, which is exactly what this decoder does. Encrypted tokens (JWE) have five segments instead of three; for those only the protected header can be shown, because the payload needs the recipient’s private key.
Reading a token
Paste the token into the input pane. Surrounding quotes, a Bearer prefix and even a full Authorization: Bearer ... header line are stripped before decoding, so you can copy straight from browser dev tools or a log line. The output is a single JSON document with header, payload and signature keys, indented with the toolbar’s Indent setting; switch to the Tree view to fold large payloads.
The info panel interprets what the raw JSON does not say plainly. Algorithm gets a name (HS256 is “HMAC with SHA-256”, RS256 is “RSA PKCS#1 v1.5 with SHA-256”), iat, nbf and exp become UTC timestamps with a relative note such as “expires in 3 hours”, and Status reads Valid, Expired, Not yet valid or Valid (no expiry). The Signature row says Not verified until you supply a key. Copy the decoded JSON with Ctrl/Cmd+Shift+C.
Checking the signature
Decoding never needs a key. To go further, fill in Verify with secret or public key (optional). It accepts three kinds of key:
- An HMAC secret for HS256, HS384 and HS512 tokens, typed exactly as the issuing service stores it.
- A PEM public key or certificate (
-----BEGIN PUBLIC KEY-----) for RS, PS, ES and EdDSA algorithms. - A JWK as JSON, for example one entry copied from an identity provider’s
/.well-known/jwks.json.
The check is done by jose, the same library many Node and edge back ends use, and the result appears both as a diagnostic and in the Signature row: Verified, or Invalid with the reason. A key whose type does not fit the header’s alg is reported as a mismatch rather than a bad signature. Tokens and keys are processed locally in the page and are not sent anywhere, which matters when the token grants access to a live system.
Warnings it raises
Beyond decoding, the tool flags tokens that would surprise a server. An exp in the past produces a warning with the exact expiry time; a future nbf says when the token becomes usable; an iat ahead of the current time points at clock skew on the issuer. If exp or iat looks like milliseconds instead of seconds, you are told to divide by 1000, a common bug in hand-rolled token code. A header with "alg": "none" is called out as unsecured, since anyone can forge such a token, and an empty signature segment is reported too.
Remember that a token decoding cleanly proves nothing about who issued it. Your API must verify the signature and check exp, aud and iss on every request, and nothing confidential belongs in a payload that any client can read.
Examples
RS256 access token from an Authorization header
The header line prefix is stripped, and the info panel lists the key ID, issuer, audience and expiry without any key being supplied.
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIwMjYtMDkta2V5In0.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c3JfNDIiLCJhdWQiOiJvcmRlcnMtYXBpIiwic2NvcGUiOiJvcmRlcnM6cmVhZCBvcmRlcnM6d3JpdGUiLCJpYXQiOjE3ODk0NzUyMDAsImV4cCI6NDEwMjQ0NDgwMH0.UmxJEapoJr6JuE5ieJlzYDoWVCzImtWEI3rtaTN7E4MU_7tsehGbYCg8XZyrZ5E0EDgMe--t9OohnyMnqCAHMWZeSebw897wsF2RN6XWp7FaoxXvjmwSLE0UKJHmxMM9BKUxv6xWdiOWxgLEWva67ItKWEvPWxUZ-BwGKxznjAEjbMd9jYwldCDdPmLalRkveCX9O8POquOK1R2_Wud37IPGcGoCnxkmWLee8om_eSIz4OQs-pgDzps6ZM2vNqUNQBi2tDTCmPDLATxS0yY_8IVWmtHEmJ1hmg4DiAeW9G8pj0ZUYIs1ZEGLJBlVGjWQTzMtWyi5hKjFWui1UMuvFA{
"header": {
"alg": "RS256",
"typ": "JWT",
"kid": "2026-09-key"
},
"payload": {
"iss": "https://auth.example.com",
"sub": "usr_42",
"aud": "orders-api",
"scope": "orders:read orders:write",
"iat": 1789475200,
"exp": 4102444800
},
"signature": "UmxJEapoJr6JuE5ieJlzYDoWVCzImtWEI3rtaTN7E4MU_7tsehGbYCg8XZyrZ5E0EDgMe--t9OohnyMnqCAHMWZeSebw897wsF2RN6XWp7FaoxXvjmwSLE0UKJHmxMM9BKUxv6xWdiOWxgLEWva67ItKWEvPWxUZ-BwGKxznjAEjbMd9jYwldCDdPmLalRkveCX9O8POquOK1R2_Wud37IPGcGoCnxkmWLee8om_eSIz4OQs-pgDzps6ZM2vNqUNQBi2tDTCmPDLATxS0yY_8IVWmtHEmJ1hmg4DiAeW9G8pj0ZUYIs1ZEGLJBlVGjWQTzMtWyi5hKjFWui1UMuvFA"
}
HS256 token verified with its shared secret
With the correct HMAC secret in the verify field, the Signature row changes from Not verified to Verified.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
},
"signature": "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
Unsigned token with alg none
The payload still decodes, but a warning explains that a token without a signature can be forged by anyone.
eyJhbGciOiJub25lIn0.eyJzdWIiOiJ1c3JfNDIifQ.{
"header": {
"alg": "none"
},
"payload": {
"sub": "usr_42"
},
"signature": ""
}
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
The token expired 2 weeks ago (2026-09-15T13:26:40Z)Explained | The exp claim is earlier than the current time. This is a warning: the token still decodes, but a server would reject it. | Request a fresh token, or use the refresh token flow. If it expired seconds after issue, check that exp was written in seconds. |
Signature does not match the supplied keyExplained | The secret or public key in the verify field is not the one that signed the token, or the header or payload was edited after signing. | Check you copied the right secret for the environment, or the JWK whose kid matches the header. Any change to the token invalidates its signature. |
A JWT has 3 dot-separated segments (header.payload.signature), or 5 for an encrypted JWE; this has 2 | The token was cut off when copied, often at a line wrap in a terminal or log viewer. | Copy the whole token again. If it was split across lines, join the pieces without spaces. |
Segment 3 (signature) contains '+', which is not valid Base64URL | The token was re-encoded with the standard Base64 alphabet, or a + was introduced by URL decoding. | JWTs use - instead of +, _ instead of / and no = padding. Copy the token from its original source rather than from a decoded URL. |
Segment 2 (payload) does not decode to UTF-8 text, so it is not JSON | The middle segment is not Base64URL-encoded JSON, so the string is not a JWT, or it is an opaque access token that only looks similar. | Confirm the token type with your identity provider. Opaque tokens can only be inspected through its introspection endpoint. |
Frequently asked questions
Can I decode a JWT without the secret key?
Yes. The header and payload are only Base64URL-encoded, so they can always be read. The key is needed only to prove the token was signed by the issuer and has not been changed.
Is it safe to paste a production token here?
Decoding and verification run in your browser and the token is not uploaded. A live token is still a credential, so avoid putting it in shared links, screenshots or tickets.
How do I verify an RS256 or ES256 token?
Paste the issuer’s public key as PEM, or one JWK from its JWKS endpoint, into the verify field. Pick the JWK whose kid matches the kid in the token header.
Why are the iat and exp values such large numbers?
JWT times are NumericDate values: seconds since 1 January 1970 UTC. The info panel converts them to readable dates, and the Unix timestamp converter does the same for any other epoch value.
Can this tool decrypt an encrypted JWE?
No. A five-segment JWE is recognised and its protected header is shown, but the payload can only be decrypted with the recipient’s private key.