A JSON Web Token, or JWT (said "jot"), is how many sites and APIs keep you signed in. After you log in the server hands your browser a token, and the browser sends it back with each request to say who you are. A JWT decoder reads the token for you: it shows the claims inside, such as who the token is for and when it runs out.
Not verified. This decodes the token and reads what it says. It does not check the signature, so it cannot tell you the token is genuine. The token stays in your browser: nothing is sent to a server, and it is not sent to our analytics either.
Header (HS256)
{
"alg": "HS256",
"typ": "JWT"
}Payload
{
"sub": "1234567890",
"name": "Coding Kitty",
"iss": "https://thecodingkitty.com",
"aud": "kitty-app",
"iat": 1767225600,
"nbf": 1767225600,
"exp": 1767229200,
"scope": "read:cats"
}Claims
subSubject1234567890
nameNameCoding Kitty
issIssuerhttps://thecodingkitty.com
audAudiencekitty-app
iatIssued at1767225600
2026-01-01 00:00:00 UTC
nbfNot before1767225600
2026-01-01 00:00:00 UTC
expExpiration time1767229200
2026-01-01 01:00:00 UTC
scopeScoperead:cats
Signature (not verified)
J_-9cZLT1xmkp9oNqc3Z17NmiEuyE0AF1cr-WC0VbOk
32 bytes. Checking it needs the secret or public key, which stay with whoever issued the token.
Runs in your browser. Nothing you paste is uploaded.
Paste a token and it decodes as you type. The header and payload are shown as
formatted JSON, every claim is listed with its meaning, and exp, iat and nbf
are shown as dates in UTC and in your own time zone. The token above is a sample
signed with a throwaway secret.
What is inside a JWT
A JWT is three parts separated by dots, each written in base64url:
| Part | What it holds | Readable here |
|---|---|---|
| Header | The signing algorithm (alg) and the token type (typ) | Yes, as JSON |
| Payload | The claims: who the token is about, who issued it, when it expires | Yes, as JSON |
| Signature | A signature over the first two parts, made with a secret or private key | Shown, not checked |
The first two parts are not secret. Base64url is a way of writing bytes as safe
text, the same idea as Base64 with - and _ in place
of + and /, and no = padding. Anyone holding the token can read them, which
is why a payload should never carry a password.
The claims you will see
| Claim | Name | What it means |
|---|---|---|
iss | Issuer | Who created and signed the token |
sub | Subject | Who the token is about, usually a user ID |
aud | Audience | Who the token is for. A service should refuse a token not meant for it |
exp | Expiration time | The token must not be accepted at or after this moment |
nbf | Not before | The token must not be accepted before this moment |
iat | Issued at | When the token was created |
jti | JWT ID | A unique ID for this token, so it can be revoked or seen only once |
Anything else is a custom claim, such as scope, email or a roles list.
Names the decoder knows get a label, and the rest are shown as they are.
How to check whether a JWT has expired
Look at exp. It is a number of seconds since 1 January 1970 UTC, so
1767229200 is 1 January 2026 at 01:00 UTC. If the time now is at or after that,
the token has expired and a server should reject it. The decoder does the
comparison against your clock and badges the exp row Expired or Not
expired. If nbf is in the future the row says Not yet valid, because the
token is not usable yet.
Two things to know when a token that looks fine is rejected. First, servers allow a
little clock skew, often a minute or so, so a token a few seconds past its exp
may still be accepted by one server and refused by another. Second, your own
clock matters here: a wrong clock on your computer shows the wrong badge.
Decoding is not verifying
Decoding reads the token. Verifying checks the signature, with the secret or public key, to prove the header and payload were not changed since the issuer signed them. This page decodes and does not verify, and it cannot, because it never has the key.
- A token that decodes can still be forged. Anyone can write a header and a payload and make them look right. Only the signature check proves who issued it.
alg: noneis a warning. A token with no signature is accepted by a badly configured server as genuine. The decoder flags it.- Decode in the browser, verify on the server. Never decide who a user is from
a decoded token in client code. Do that with a library on the server that
checks the signature, the
exp, theissand theaud.
Gotchas
- Expiry is in seconds. JavaScript's
Date.now()is in milliseconds, so compareexpwithDate.now() / 1000. - A JWT is not encrypted. A signed token is readable by anyone who sees it. Treat a live one like a password, and prefer expired or test tokens when you paste one anywhere.
- Five parts means encrypted. A JWE has five dot-separated parts and cannot be decoded without its key.
- A
Bearerprefix is fine. Copied from anAuthorizationheader, it is stripped for you, as are quotes and line breaks. - Opaque tokens are not JWTs. Some services issue a random string with no dots. There is nothing to decode in one, because the meaning lives on the server.
Related
- Base64 Encoder and Decoder, the encoding each JWT part is written in. Switch it to URL-safe for base64url.
- JSON Formatter, for the payload once you have copied it.
- Hash Generator, which makes the HMAC signatures an HS256 token is signed with.
- URL Encoder and Decoder, for a token that has travelled through a query string.
- Password Generator, for a long random signing secret.
- The JavaScript cheat sheet for
JSON.parse,atobandDate.
Common questions
How do I decode a JWT?
Paste the token into the box above. It is split at the two dots into a header, a payload and a signature. The header and payload are base64url text that holds JSON, so the decoder reads them back and shows them formatted, with the exp, iat and nbf claims as dates. The signature is binary and is shown as it is.
Is this JWT token decoder online safe to paste a token into?
The decoding runs in your browser and the token is not sent to a server or to our analytics. We record that the tool was used and how long the paste was, never what it said. Even so, treat a live token like a password: it lets whoever holds it act as you until it expires, so prefer a test token or one that has already expired.
Does the decoder verify the signature?
No, and it cannot. Checking a signature needs the secret key (for HS256) or the public key (for RS256 and ES256), and that stays with the service that issued the token. The decoder only reads what the token says about itself, so a forged token decodes just as well as a real one. Never trust a token because it decodes.
Is a JWT encrypted?
Usually not. A normal signed JWT (a JWS) is only encoded: anyone who has it can read the header and payload, which is exactly what this JWT parser does. Do not put secrets in a payload. An encrypted JWT (a JWE) has five parts instead of three and cannot be read without the key, and this decoder says so when it sees one.
How do I tell whether a JWT has expired?
Read the exp claim. It is a time in seconds since 1 January 1970 UTC, and a token is not to be accepted at or after that moment. The decoder shows exp as a date, compares it with your clock, and puts an Expired or Not expired badge on it. A token whose nbf (not before) is still in the future is marked Not yet valid.
What do iss, sub, aud, exp, nbf, iat and jti mean?
They are the registered claims. iss is the issuer that created the token, sub the subject it is about (usually a user ID), aud the audience it is meant for, exp when it expires, nbf the time before which it is not valid, iat when it was issued and jti a unique ID for the token. The decoder labels each one it finds in the Claims list.
Why does my JWT say it is not valid base64url?
Each part of a JWT uses the URL-safe alphabet: A to Z, a to z, 0 to 9, a hyphen and an underscore, with no equals padding. A plus, a slash or an equals sign means the part was made with ordinary Base64, and any other character usually means the token was cut, quoted or had extra text pasted in. The error names the character and where it is.
Why does it say my token has the wrong number of parts?
A signed JWT is exactly three parts joined by two dots. Two parts usually means the signature was cut off, and one means this is not a JWT at all, perhaps an opaque access token or an API key. Five parts is an encrypted JWT. A leading Bearer prefix or surrounding quotes are removed for you, but anything else glued to the token will break it.
Why are the dates in seconds and not milliseconds?
The JWT standard defines exp, nbf and iat as a count of seconds, not the milliseconds JavaScript uses for Date.now(). A value like 1767229200 is a normal date in 2026, while the same number with three more digits would be far in the future. If a decoded date looks like the year 55000, the token was probably issued with milliseconds by mistake.