---
title: "JWT decoder"
canonical: "https://devtools.nicxon.tech/tools/jwt-decoder/"
source: "https://devtools.nicxon.tech/tools/jwt-decoder/"
generator: devtools.nicxon.tech
---

# JWT decoder

> Decode a JSON Web Token's header and payload, expand standard claims, and flag alg:none and expired tokens.

## What this does

A JSON Web Token is three base64url-encoded segments joined by dots:
 `header.payload.signature`. This tool splits the token, decodes
 the header and payload to readable JSON, and explains the registered
 claims — `iss`, `sub`, `aud`,
 `exp`, `nbf`, `iat`, `jti` —
 converting the numeric timestamps to readable UTC. It then runs a few
 safety checks and, for HMAC tokens, can verify the signature if you supply
 the secret.

All of this happens in your browser. You can paste a live session token
 from your own application to see exactly what your auth server put in it.

## Reading the signature segment

The third segment is a signature or a MAC over
 `base64url(header) + "." + base64url(payload)`. The
 `alg` field in the header says how it was produced:

- **HS256 / HS384 / HS512** — HMAC with a shared secret. The same secret both signs and verifies, so it must never be shipped to a browser or a third party.
- **RS256 / PS256 / ES256** — asymmetric. The issuer signs with a private key; anyone can verify with the public key. This is what you want when the verifier and the signer are different parties.
- **none** — no signature at all. Treat its presence on an auth token as a bug or an attack.

Decoding never checks the signature by itself. A JWT you pulled from a log
 is just as decodable whether or not it was tampered with — the decoded
 payload tells you what someone *claims*, not what is *true*.
 Only signature verification against the correct key makes it trustworthy.

## Why <code>alg: none</code> and algorithm confusion matter

Two classic JWT vulnerabilities both come from trusting the token to tell
 you how to verify it. The first is `alg: none`: if your library
 honours it, an attacker strips the signature, edits the payload, and walks
 in. The second is **RS256-to-HS256 confusion**: the server
 expects an RS256 token and verifies with the RSA public key, which is not
 secret. An attacker changes the header to `HS256` and signs the
 token using that public key's bytes as the HMAC secret. A naïve verifier
 that reads `alg` from the token then "verifies" the forgery.
 The fix for both is the same: the verifier decides the algorithm and the
 key out of band, and rejects any token whose header disagrees.

## Worked example

Load the sample token. The header is
 `{"alg":"HS256","typ":"JWT"}` and the payload includes
 `"iss":"auth.example.com"`, `"aud":"api"`,
 `"exp":1700003600`, and `"role":"admin"`. The claims
 table converts `exp` to a UTC time and explains that
 `aud` must equal your service's identifier or the token should
 be rejected. Because that sample `exp` is in the past, the
 status line reports the token as expired. Enter any secret in the verify
 field and the tool reports the signature does not match — the sample
 signature is illustrative, not real.

## Limits and gotchas

- **No asymmetric verification.** RS/PS/ES signatures can't be checked here without the issuer's public key; only HS256/384/512 are supported.
- **Clock skew.** The expired / not-yet-valid checks use your device clock. Real verifiers usually allow a small leeway (30–60 seconds).
- **Decoding is not validation.** A well-formed token can still be forged, revoked, or issued by the wrong party.
- **Encrypted tokens (JWE)** have five segments and are not decodable without the key; this tool handles signed tokens (JWS) only.
- **Sensitive payloads.** Anything in the payload is readable by anyone who holds the token. Don't put secrets in claims.

## How to use

- **Paste the token** — Paste the full JWT — three dot-separated segments — into the input box.
- **Read the header and payload** — The decoded header and payload JSON appear side by side. Registered claims are explained in the table below.
- **Check the warnings** — The status line flags alg:none, a missing exp claim, an expired token, and a not-yet-valid (nbf) token.
- **Optionally verify the signature** — For HS256/384/512 tokens, paste the shared secret to check the signature locally. The secret is never sent anywhere.

## Frequently asked questions

### Does decoding a JWT here reveal it to anyone?

No. The token is split and base64url-decoded in your browser with JavaScript. Nothing — not the token, not the payload, not a secret you enter for verification — is sent to a server. That is the point of doing it client-side: you can safely inspect a real production token.

### Why is "alg": "none" dangerous?

A JWT with alg: none has no signature. The standard allows it for cases where the token's integrity is guaranteed by other means, but if an application accepts it on a normal auth path, an attacker can craft any payload they like — admin: true, someone else's user ID — and it will be trusted. Several real libraries historically accepted none by default. Your verification code must pin the expected algorithm and reject anything else, including none.

### The payload looks fine but is the token actually valid?

Decoding only proves the token is well-formed. A token is valid only if the signature verifies against the issuer's key and the claims check out: exp in the future, nbf in the past, iss is who you expect, and aud contains your service. This tool checks the time-based claims and can verify an HMAC signature; it cannot verify RS/ES/PS signatures because it doesn't have the issuer's public key.

### What is the difference between the "alg none" warning and an expired warning?

"alg none" is a structural red flag about how the token is signed — it should almost never appear on a real token. "Expired" means the token was legitimately issued but its exp timestamp has passed, so a correct server will now reject it. Both cause the status line to turn amber, but they are different problems: one is a possible attack, the other is normal token lifecycle.

---
*Source: [https://devtools.nicxon.tech/tools/jwt-decoder/](https://devtools.nicxon.tech/tools/jwt-decoder/) — generated by devtools.nicxon.tech.*
