JWT Decoder

Paste a JSON Web Token and inspect its header, payload, and signature segment locally in your browser.

Runs in your browser No upload required Decode
Decode JWT header and payload locally. This tool does not verify signatures.

 
 

How to use JWT Decoder

  1. Paste a JWT with three dot-separated segments.
  2. Select Decode token.
  3. Read the formatted header and payload JSON.
  4. Use the signature segment only as a visual reference; this tool decodes only and does not verify signatures.

Why this tool exists

A JWT decoder helps you inspect the structure of a JSON Web Token. JWTs are commonly used for authentication, session state, API authorization, and service-to-service claims. A token normally has three dot-separated segments: header, payload, and signature. This tool decodes the first two segments so you can read the algorithm, token type, issuer, subject, audience, expiration, and custom claims.

Decoding a JWT is not the same as verifying it. The header and payload are Base64URL-encoded JSON, which means they are meant to be readable by anyone who has the token. The signature proves whether the token was issued by a trusted party, but verifying that signature requires the correct secret or public key. This tool intentionally avoids accepting verification keys, keeping it focused on safe local inspection — verify signatures in your server-side auth library instead.

Treat real JWTs as sensitive. A token can grant access even when the payload looks harmless. If a token came from a production browser session, a mobile app, a server log, or a customer support conversation, do not paste it into random online tools. CleanWebTools decodes locally in the browser, but your operational habit should still be conservative: redact, use test tokens where possible, and clear the input after inspection.

Use this page when you need to quickly understand claims, expiration timestamps, or token shape while debugging. If you need to format nested JSON after decoding, use the JSON Formatter page.

Common examples

  • Input: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkphbmUiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c → header decodes to {"alg":"HS256","typ":"JWT"}, payload to {"sub":"1234","name":"Jane","iat":1516239022}, and the 43-char third segment is the raw 32-byte HMAC-SHA256 signature.
  • An alg:none unsecured token looks like eyJhbGciOiJub25lIn0.eyJhZG1pbiI6dHJ1ZX0. — note the trailing dot with nothing after it. It still has 3 segments, but the signature is empty; a verifier that blindly obeys the header's declared alg skips signature checking and accepts the forged admin:true payload.
  • Converting iat 1516239022 (seconds) gives 2018-01-18T01:30:22Z. If you mistakenly wrote Date.now() there, you'd get 1516239022000 — interpreted as seconds that lands around the year 50000, i.e. a token that for all practical purposes never expires.

FAQ

Why does my decoder produce mangled UTF-8 when I run a JWT segment through atob() or base64_decode()?

Because a JWT segment is Base64url (RFC 4648 section 5), not standard Base64. The '-' character stands in for '+', '_' stands in for '/', and all trailing '=' padding is stripped per RFC 7515. Standard atob() expects the '+/' alphabet with padding, so you must swap '-'→'+' and '_'→'/' and re-pad the string to the next multiple of 4 before decoding. A remainder of 1 char is invalid and never legitimately occurs.

What's the difference between exp, nbf, and iat in a decoded payload?

All three are NumericDate values (RFC 7519 section 2): seconds since the Unix epoch, expressed as a plain JSON number. exp is the expiration time (token invalid after it), nbf is not-before (token invalid until it), and iat is issued-at (when the token was minted). They are in SECONDS, not milliseconds; dropping a JavaScript Date.now() value (which returns milliseconds) straight into exp makes the timestamp roughly 1000x too large, yielding a token that effectively never expires.

If a decoder shows me admin:true in the payload, can I trust that claim?

No. Decoding only Base64url-decodes the header and payload for display; it does not verify the signature. Anyone can edit the payload, re-encode it, and the decoded view looks identical. The signature is computed over the ASCII string base64url(header) + '.' + base64url(payload), so only a server-side check against the secret (HS256) or public key (RS256) proves the claims weren't tampered with. Honoring decoded claims without that verification is the root cause of most JWT auth bypasses.