Why use JWT Decoder
This JWT decoder splits a JSON Web Token at its dots, Base64URL-decodes the first two parts and shows the header and payload as formatted JSON. A Claims panel pulls out the algorithm, subject, issuer and audience, and converts the iat, nbf and exp timestamps from Unix seconds into UTC date and time, so exp 1700000000 reads as 2023-11-14 22:13:20 UTC. No secret or key is needed, because the header and payload of a signed token are encoded, not encrypted. The signature is not verified, so a decoded token tells you what it claims, not whether it is genuine.
Decoded JWT examples
The first two are complete test tokens; paste either into the box above to reproduce the result. The third describes the decoded contents of a typical access token.
The widely used HS256 sample token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header {"alg":"HS256","typ":"JWT"}. Payload {"sub":"1234567890","name":"John Doe","iat":1516239022}. Claims: Algorithm HS256, Subject 1234567890, Issued at 2018-01-18 01:30:22 UTC.
There is no exp claim, so no Expires line appears. A token without exp never expires on its own.
A token with an expiry one hour after issue
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImV4cCI6MTcwMDAwMDAwMCwiaWF0IjoxNjk5OTk2NDAwfQ.c2lnbmF0dXJl
Header {"alg":"HS256","typ":"JWT"}. Payload {"sub":"42","exp":1700000000,"iat":1699996400}. Claims: Algorithm HS256, Subject 42, Issued at 2023-11-14 21:13:20 UTC, Expires 2023-11-14 22:13:20 UTC.
exp minus iat is 3600 seconds, a one-hour lifetime. The signature part of this token is a placeholder, and it still decodes, which shows that decoding proves nothing about authenticity.
An access token from an identity provider
Header {"alg":"RS256","typ":"JWT","kid":"2026-key-1"} and a payload with iss https://auth.example.com, sub user_8421, aud https://api.example.com, iat and nbf 1791451800, exp 1791455400, scope "read:orders write:orders"
Claims: Algorithm RS256, Subject user_8421, Issuer https://auth.example.com, Audience https://api.example.com, Issued at 2026-10-08 09:30:00 UTC, Not before 2026-10-08 09:30:00 UTC, Expires 2026-10-08 10:30:00 UTC.
scope and kid are not in the Claims panel; read them in the Payload and Header JSON.
JWT registered claims and what they mean
The seven claims registered in RFC 7519, section 4.1. All are optional in the specification; the issuer decides which to include.
| Claim | Name | Value | Shown in the Claims panel as |
|---|---|---|---|
| iss | Issuer | String or URL identifying who created the token | Issuer |
| sub | Subject | Who the token is about, usually a user or client ID | Subject |
| aud | Audience | The recipient the token is meant for; a string or an array of strings | Audience (array items joined by commas) |
| exp | Expiration time | Unix time in seconds on or after which the token must be rejected | Expires, as a UTC date and time |
| nbf | Not before | Unix time in seconds before which the token must be rejected | Not before, as a UTC date and time |
| iat | Issued at | Unix time in seconds when the token was created | Issued at, as a UTC date and time |
| jti | JWT ID | Unique identifier for the token, used to detect replays | Not listed; read it in the Payload |
Other claims you will often see are not part of this list: scope, azp, nonce, name, email, roles and anything else the issuer adds. They appear in the Payload JSON exactly as issued.
Reading exp and iat: token lifetimes in seconds
Subtract iat from exp to get the lifetime the issuer chose. Common values:
| exp minus iat (seconds) | Lifetime | Typical use |
|---|---|---|
| 300 | 5 minutes | Short-lived access tokens, one-time links |
| 900 | 15 minutes | Access tokens paired with a refresh token |
| 3600 | 1 hour | A common default lifetime for access tokens |
| 28800 | 8 hours | A working-day session |
| 86400 | 24 hours | Daily sign-in |
| 604800 | 7 days | Remember-me sessions |
| 2592000 | 30 days | Long-lived refresh tokens |
Time claims are in seconds, not milliseconds. A 13-digit exp such as 1791455400000 was produced with a millisecond clock by mistake; the decoder then shows a garbled Expires value beginning +058738, a year tens of thousands of years in the future. A time claim written as a string, such as "1791451800", is not converted at all.
What the alg value in the header means
| alg | Algorithm | Key used to verify |
|---|---|---|
| HS256 | HMAC with SHA-256 | The same shared secret that signed the token |
| HS384 / HS512 | HMAC with SHA-384 or SHA-512 | Shared secret |
| RS256 | RSA PKCS#1 v1.5 signature with SHA-256 | The issuer's RSA public key |
| PS256 | RSA-PSS signature with SHA-256 | The issuer's RSA public key |
| ES256 | ECDSA on the P-256 curve with SHA-256 | The issuer's elliptic-curve public key |
| EdDSA | Edwards-curve signature, usually Ed25519 | The issuer's Ed25519 public key |
| none | No signature at all | Nothing to verify; servers should refuse these |
The header often also carries typ, usually JWT, and kid, the ID of the key that signed the token, which a server uses to pick the right public key from the issuer's key set. A server must decide which algorithms it accepts and never take that decision from the token's own header.
Why a token will not decode
| What you pasted | Message | Fix |
|---|---|---|
| Bearer eyJhbGci... (the whole Authorization header value) | The header is not valid Base64URL-encoded JSON. | Delete Bearer and the space so the text starts with eyJ |
| Text with no dots: a cut-off token, or an opaque access token or API key | A JWT has three dot-separated parts: header.payload.signature. | Copy the full token; if it never had dots it is not a JWT and only its issuer can say what it represents |
| A five-part encrypted token (JWE) | The payload is not valid Base64URL-encoded JSON. | It cannot be read without the decryption key; decrypt it where the key lives |
| Dotted text that is not a token, such as abc.def.ghi | The header is not valid Base64URL-encoded JSON. | Check you copied the token and not a hostname, version string or file name |
| A token truncated in the middle of the payload | The payload is not valid Base64URL-encoded JSON. | Copy it again from the source, checking for a trailing ellipsis |
A token with only header and payload and no signature part is still decoded, as is one whose alg is none.
Is it safe to paste a JWT into an online decoder?
A JWT used as a bearer token is a credential: whoever holds it can act as its subject until it expires. This page decodes the token with JavaScript in your browser and makes no request containing it, so it does not leave your device. That is the property to look for in any decoder you use.
Even so, a sensible habit is to decode expired tokens or tokens from a test environment where you can, and to treat a live production token that has been pasted into any tool, shared in a chat or written to a log as one to revoke.
Never paste a signing secret or private key into a web page. This decoder has no field for one, and that is deliberate: verification belongs in your server or in a local script using a maintained JWT library, where the key never leaves your control.
How to get your result
- 1
Paste the token into JWT. Remove the word Bearer and the space after it if you copied an Authorization header; line breaks inside the token are fine.
- 2
Read the Claims panel for Algorithm, Subject, Issuer, Audience, Issued at, Not before and Expires. Only claims present in the token are listed.
- 3
Compare Expires with the current UTC time to see whether the token has lapsed. The decoder shows the time but does not label the token expired or valid.
- 4
Scroll to Header and Payload for the full JSON, including custom claims such as scope or roles, and copy either block.