"category": "inspect-and-test",

JWT Decoder

"tldr": Paste a JWT and instantly see its decoded header and payload with expiry status - fully local, the token never leaves your browser.

Paste a JSON Web Token and instantly see its decoded header and payload, with the time claims (exp, iat, nbf) converted to human-readable dates and a clear expired / not-expired badge. Decoding happens entirely in your browser - the token is never transmitted, which matters because JWTs are credentials.

{"tool": "jwt-decoder"}

A JWT is three Base64url segments separated by dots: header (algorithm and type), payload (the claims), and signature. The first two are just encoded JSON - anyone can read them, which is why tokens must never carry secrets. The signature proves integrity but requires the key to verify; this tool decodes and inspects, it does not verify.

How to decode a JWT

  1. 1Paste the token - all three dot-separated segments, copied completely.
  2. 2The header and payload decode instantly; the claims panel pretty-prints the JSON.
  3. 3exp, iat, and nbf are shown as UTC dates with an expired/valid badge computed from exp.
  4. 4Copy the payload JSON for debugging - and treat the token itself as a secret while you do.

Convert JWT to Claims in code

Verify properly (Node.js)
import jwt from "jsonwebtoken"; // npm install jsonwebtoken

try {
  const claims = jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ["HS256"], // always pin the algorithm
  });
  console.log(claims.sub);
} catch (e) {
  // TokenExpiredError | JsonWebTokenError
}
Verify properly (Python)
import jwt  # pip install PyJWT

try:
    claims = jwt.decode(token, key, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
    ...  # expired
except jwt.InvalidTokenError:
    ...  # bad signature / malformed

Frequently asked questions

Is it safe to paste a real token here?

Decoding runs locally with no network requests - nothing is transmitted or logged. That said, a JWT is a live credential until it expires: prefer expired or staging tokens when debugging, and clear your clipboard after copying payloads.

Why does decoding work without the secret key?

Because JWTs are signed, not encrypted. Base64url is an encoding anyone can reverse; the secret is only needed to create or verify the signature. If you need the contents hidden, that is JWE (encrypted JWT) - which this tool cannot open, by design.

What do exp, iat, nbf, sub, and aud mean?

They are registered claims from RFC 7519: exp = expiry time, iat = issued at, nbf = not valid before (all Unix timestamps in seconds), sub = subject (usually the user ID), aud = intended audience, iss = issuer. Everything else in the payload is application-defined.

My token says "expired" but my server still accepts it. Why?

Common causes: the server allows clock skew (often 30-300 seconds), it is not validating exp at all (a real vulnerability worth reporting), or it checks a different token than the one you decoded. The reverse case - valid here, rejected there - usually means the server rejects the signature or audience, not the expiry.

How do I verify a JWT signature properly?

On a server, with a library: jsonwebtoken (Node), PyJWT (Python), golang-jwt (Go) - passing the expected algorithm explicitly to prevent alg-swap attacks. Never verify in the browser (the key would be exposed) and never trust decoded claims that have not been verified server-side.

Last updated:

You might also need

Free and in-browser, like everything on JSON Console. Browse all tools →