JWT Debugger
Decode, verify and debug JSON Web Tokens.
Encoded Token
Header
// HeaderPayload
// PayloadJSON Web Tokens: structure, claims, algorithms and the mistakes that get exploited
A JSON Web Token (JWT, RFC 7519) is three base64url segments joined by dots: header.payload.signature. The header names the signing algorithm and usually a key id, the payload is a JSON object of claims, and the signature covers the first two segments exactly as they appear in the token. The encoding is base64url with the padding removed (RFC 7515), which is why a token starts with eyJ: that is {" encoded. The signed form is technically a JWS; an encrypted JWT (JWE, RFC 7516) has five segments and cannot be read without the key.
The registered claims
| Claim | Meaning | Notes |
|---|---|---|
iss | Issuer | Who minted the token, usually a URL. Verify it matches the server you trust. |
sub | Subject | The user or client the token is about. |
aud | Audience | Which API the token is for. A token for service A must be rejected by service B. |
exp | Expiry | Seconds since the Unix epoch, not milliseconds. Reject after this time. |
nbf | Not before | Reject before this time. |
iat | Issued at | Useful for detecting tokens minted before a password change. |
jti | JWT id | Unique id, the hook for revocation lists. |
The three time claims are NumericDate values in seconds. Paste one into the epoch converter to read it; a 13-digit value there means someone used Date.now() without dividing by 1000, and every verifier will treat the token as valid for about 50,000 years.
What this debugger does, and what it does not
The page uses the jwt-decode library to split the token and decode the header and payload, then pretty-prints both as JSON. The input turns green when the token has the right shape and red with "Invalid JWT Token" when it does not: a missing segment, a segment that is not valid base64url, or a payload that is not JSON. A JWE will also be rejected because it has five segments.
Decoding is not verifying
Anyone can decode a JWT; the payload is only encoded, never hidden. This tool does not check the signature or the expiry, and it never asks for your secret or private key. Verification belongs on the server, in a library that pins the expected algorithm and validatesexp, nbf, iss and aud.Algorithms
HS256 is HMAC with SHA-256 over a shared secret. It is symmetric: every service that can verify a token can also mint one, so the secret must be long (at least 32 random bytes) and shared with as few parties as possible. Short, guessable secrets can be recovered offline from a single captured token with tools like hashcat. RS256 is RSA (PKCS#1 v1.5) with SHA-256, PS256 is RSA-PSS, ES256 is ECDSA on the P-256 curve, and EdDSA uses Ed25519 (RFC 8037). These are asymmetric: the issuer signs with a private key and verifiers only need the public key, typically fetched from a JWKS endpoint such as /.well-known/jwks.json and selected by the header's kid. Use an asymmetric algorithm whenever more than one service verifies tokens.
Attacks worth knowing by name
- alg none. A header of
{"alg":"none"}declares an unsigned token. Early libraries accepted it, letting anyone forge any payload. Modern libraries refuse it, but only if you do not pass it in your allowed-algorithms list. - Algorithm confusion. If a verifier accepts both RS256 and HS256 and uses "whatever the header says", an attacker can take the public RSA key, use it as an HMAC secret, and mint HS256 tokens the server accepts. Pin one algorithm per key.
- Weak HMAC secrets. hashcat mode 16500 brute-forces HS256 secrets from a captured token. Use random secrets, not words.
- Missing claim checks. Verifying the signature but not
exporaudmeans old tokens live forever and a token for one API opens another. - Untrusted
kid,jkuandx5u. These header fields tell the verifier where the key is. If they are used to build a file path, a SQL query or an outbound HTTP request without validation, the attacker chooses the key. - Replay after logout. A signed token stays valid until
exp. Keep access tokens short-lived (minutes), rotate refresh tokens, and keep a denylist keyed byjtifor forced logout.
RFC 8725 (JSON Web Token Best Current Practices) collects the defences for all of these and is short enough to read in one sitting. The JWT security guide on this site walks through the same points with code.
Storage and lifetime in the browser
A token in localStorage can be read by any script that runs on the page, including an injected one, so an XSS bug becomes a stolen session. An HttpOnly, Secure, SameSite=Lax or Strict cookie cannot be read by JavaScript, at the cost of needing CSRF defences for state-changing requests. Cookies are also capped at about 4 KB and proxies limit total header size (often 8 KB), which is a reason to keep claims small and put roles in a lookup rather than in the token. Allow a small clock skew (30 to 60 seconds) when checking exp and nbf; two servers with drifting clocks is a common cause of "token expired" the moment it is issued.
Command-line equivalents
# Decode the payload (no verification)
node -e "console.log(JSON.parse(Buffer.from(process.argv[1].split('.')[1],'base64url')))" "$TOKEN"
python3 -c "import jwt,sys; print(jwt.decode(sys.argv[1], options={'verify_signature': False}))" "$TOKEN" # pip install PyJWT
# Recompute an HS256 signature by hand and compare it with the third segment
printf '%s' "$HEADER.$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64 | tr '+/' '-_' | tr -d '='How to use
- Paste the whole token (the string with two dots, usually starting with eyJ) into the Encoded Token box.
- The Header and Payload panels decode immediately and show pretty-printed JSON; the box turns green for a well-formed token and red for a malformed one.
- Read the standard claims: iss, sub, aud, exp, nbf, iat and jti, plus any custom claims such as roles or scopes.
- Convert exp or iat with the Epoch Converter to see the real date, and check the alg and kid fields in the header against what your server expects.
- To verify the signature, use your server-side library with the secret or public key. This page intentionally never asks for keys.
Common use cases
- Diagnosing a 401 response by checking whether the token is expired, has the wrong audience, or lacks a required scope.
- Inspecting what an API gateway or identity provider (Auth0, Keycloak, Cognito, Firebase) actually puts in its tokens.
- Confirming that exp is in seconds and not milliseconds after a change to token-issuing code.
- Reading the kid header to work out which key in a JWKS document should verify a token.
- Reviewing a token during a security assessment for secrets, personal data or an alg of none.
- Teaching new team members the difference between a decoded token and a verified one.
Frequently asked questions
Is it safe to paste a real JWT here?
Does this tool verify the signature?
What does "alg": "none" mean and why is it dangerous?
What should never go into a JWT payload?
Why does my server say the token is expired when the time looks right?
How do I log a user out if tokens are stateless?
HS256 or RS256, which should I choose?
Why does the debugger reject my token as invalid?
Advertisement