JWT Decoder
Read a JSON Web Token: its header, its claims exactly as written, its signature and its times, with a warning for anything a server would refuse. Nothing is verified and nothing leaves the browser.
Overview
A JSON Web Token (JWT, RFC 7519) carries claims, such as who a user is and until when the token is valid, from one party to another. A signed token (JWS, RFC 7515) has three parts separated by dots: a header that names the algorithm, the payload with the claims, and a signature over both. Header and payload are JSON written in Base64url, so anyone holding the token can read them. The signature only shows that nobody changed them.
An encrypted token (JWE, RFC 7516) has five parts. Its header is readable too, but its payload can only be read with the recipient's key.
Web interface
Paste a token into the input; header and payload appear as you type. The hints give the algorithm, type and key id of the header and the number of claims, and the line under the output describes the signature, the times and any warnings. Copy Header and Copy Payload copy the JSON, Clear empties everything, Ctrl/⌘ + Enter decodes again.
The dice in the corner of the input loads a random example, a different one each time; see Examples.
What the input may be
Tokens rarely arrive clean. The input accepts them as they are copied:
- behind
Beareror a wholeAuthorization: Bearerheader line; - in double or single quotes, as in JSON or a configuration file;
- wrapped over several lines: all spaces and line breaks are removed.
The hint above the input says what was removed. A token in the standard Base64 alphabet, with
+ and / instead of - and _, is read with a warning, and
= padding at the end is accepted.
Header and payload
Header and payload are shown as JSON exactly as they stand in the token, only indented. The numbers are not
converted, so 12345678901234567890 keeps every digit, where a JavaScript number would turn it
into 12345678901234567000. Escapes and keys that appear twice stay as they are too.
A payload that is not JSON is shown as text: that is a signed message (JWS), but not a JWT. A payload that
is JSON but not an object, such as null or a list, is shown with a warning, since JWT claims have
to be an object.
Times and warnings
The three time claims are shown in UTC, with how long ago or how far ahead they are:
| Claim | Shown as | Warning when |
|---|---|---|
iat, issued at | Issued 2026-09-12 14:03:00 UTC, 2 hours ago. | it lies in the future |
nbf, not before | Valid from 2026-09-12 14:03:00 UTC. | it lies in the future: the token is not valid yet |
exp, expires | Expires 2026-10-12 14:03:00 UTC, in 29 days. | it has passed: the token is expired |
JWT times are seconds since 1970. A time that is not a number is reported, and so is a number above
100 billion, which as seconds would lie beyond the year 5000 and is almost always milliseconds. A token without
exp is noted as having no expiry time. The payload hint adds expired,
not valid yet or unsigned to the number of claims.
Signatures
The signature is not verified. For an HMAC token (HS256, HS384,
HS512) that would take the shared secret, for an RSA, ECDSA or EdDSA token the issuer's public
key. The line under the output names the algorithm and the size of the signature, and warns when:
- the header says
"alg": "none": the token is unsigned, and anyone can change it; - the header names no algorithm;
- the signature is empty although the header names an algorithm;
- the size does not fit the algorithm. HMAC and ECDSA signatures have a fixed size: 32, 48 and 64 bytes
for
HS256,HS384,HS512, and 64, 96 and 132 bytes forES256,ES384,ES512.
Examples
The dice builds a new example on each click, so its times are relative to the moment you roll:
| Example | What it shows |
|---|---|
| valid for an hour | an ordinary HS256 token |
| expired | exp one day ago, with issuer and audience |
| not valid yet | nbf one day ahead |
| HS512 with key id and two audiences | a kid in the header and aud as a list |
| ES256 without typ | an ECDSA token whose header names no type |
| unsigned (alg: none) | a token without a signature |
| a number too large for JavaScript | 12345678901234567890, kept digit for digit |
| encrypted (JWE) | a token with five parts whose payload cannot be read |
The HMAC examples are signed with the secret troubleshooting.tools sample secret, so they can
be verified elsewhere. The ES256 example is signed and the JWE example encrypted with a key that is made on the
spot and thrown away: nobody can verify or read them, which for the JWE is the point.
Errors
A token that cannot be read is not shown half-way. The output stays empty and the line under it says why:
| Message | Meaning |
|---|---|
a JWT has three parts separated by dots, five when it is encrypted; this has 2 | Part of the token is missing, or it is no JWT. |
the header contains “*”, which is not a Base64url character | A character that has no place in a token, named in the message. |
the header is one character too long or too short for Base64url | The token was cut off or a character was lost when copying. |
the header is not JSON, the header has to be a JSON object | The first part decodes, but not to a header. |
the payload is not valid UTF-8 text | The part decodes to bytes that are no text. |
No API
There is no API for this tool, and there should not be one: a token is a credential, and pasting it into a service is handing it over. Decoding runs in the browser; in a program, use a JWT library of the language at hand, which can also verify the signature.
Limits
The input has no length limit. The examples are signed and encrypted with the browser's own cryptography (Web Crypto), which browsers only offer on HTTPS pages; where it is missing, the dice is hidden and pasted tokens are decoded all the same.
Privacy
Nothing leaves the browser. The token is decoded on the page, nothing is stored, and Copy writes to the clipboard only when you press it. Still, treat a real token like a password: anyone who has it can use it until it expires.