JWT Decoder

Decode a JSON Web Token's header and payload, read its claims and check whether it has expired.

Decoding is not verification. Anyone can read and forge the contents of a JWT; only a signature check with the right key proves who issued it. Tokens are decoded in your browser and never sent anywhere.

Decoded
Verify HMAC signature

Overview

A JSON Web Token is a compact, URL-safe string that carries a set of claims, such as who the user is, who issued the token and when it expires. APIs receive them in the Authorization: Bearer header, and OpenID Connect providers return them as ID tokens. When a request fails with 401 or 403, the first debugging step is usually to look inside the token and compare its claims with what the server expects.

Paste a token and this decoder shows the header and payload as formatted JSON, explains every registered claim, converts exp, nbf and iat to readable dates with the time remaining, and flags problems like alg: none, millisecond timestamps and clock skew. For HMAC-signed tokens you can also check the signature with a secret, using the browser's Web Crypto API.

How a JWT is structured

A signed JWT (RFC 7519, using the JWS format from RFC 7515) is three Base64URL strings joined by dots:

header.payload.signature

header    {"alg":"HS256","typ":"JWT"}            -> eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
payload   {"sub":"user_8f3a2c71","exp":4102444800} -> eyJzdWIiOiJ1c2VyXzhmM2EyYzcxIiwiZXhwIjo0MTAyNDQ0ODAwfQ
signature HMAC-SHA256(header + "." + payload, secret), Base64URL-encoded

The header names the signing algorithm (alg) and often a key ID (kid) that tells the verifier which key to use. The payload holds the claims. The signature is computed over the exact first two segments, so changing a single character in either one invalidates it. Since every eyJ you see is just {" in Base64, the header and payload are readable by anyone who holds the token.

A token with five segments is a JWE, an encrypted JWT. Its payload is ciphertext, and no decoder can show it without the recipient's key.

Registered claims

ClaimNameWhat servers check
issIssuerMust exactly match the expected issuer URL, including any trailing slash.
subSubjectThe user or client the token is about.
audAudienceA string or array; the receiving API must find its own identifier in it.
expExpiration timeReject when the current time is at or after this value.
nbfNot beforeReject when the current time is before this value.
iatIssued atUsed for age limits; some libraries reject values in the future.
jtiJWT IDUnique ID, used to detect replay or to revoke a single token.

The three time claims are NumericDate values: seconds since 1970-01-01 UTC, not milliseconds. Writing Date.now() instead of Math.floor(Date.now() / 1000) creates a token that expires in the year 55,000. Convert individual values with the epoch converter. Everything else in the payload is a custom claim, such as email, roles or scope.

Because the payload is only encoded, never put passwords, API keys or sensitive personal data in it. Assume that every proxy log, browser extension and support ticket that sees the token can read it.

HS256 vs RS256 vs ES256

  • HS256 is HMAC with SHA-256. The same shared secret signs and verifies, so every service that can check a token can also mint one. It fits a single backend. RFC 7518 requires a key of at least 256 bits; short passwords can be brute-forced offline from any captured token.
  • RS256 is RSA with SHA-256. The issuer signs with a private key and publishes the public key, usually as a JWKS document. Any number of APIs can verify without being able to issue tokens. This is the common default for identity providers.
  • ES256 is ECDSA on the P-256 curve. It has the same public-key model as RS256 with much shorter keys and signatures (64 bytes instead of 256 for RSA-2048).

Whatever the algorithm, the server should accept only the algorithms it expects. Letting the token's own alg header pick the algorithm is how alg: none and RS256-to-HS256 confusion attacks work.

Debugging a 401 with a valid-looking token

  • Expired: exp is in the past. Access tokens often live for 5–60 minutes, so a token copied from a log an hour ago is usually dead. Use the refresh flow rather than extending the lifetime.
  • Clock skew: a token rejected right after login, with an error such as jwt not active (Node's jsonwebtoken) or The token is not yet valid (iat) (PyJWT), means the API server's clock is behind the issuer's. Fix NTP on the server, and allow a small leeway (30–60 seconds is common) in the validation settings.
  • Wrong audience: the token was issued for a different API, often an ID token sent where an access token was expected. Compare aud with the audience your API is configured for.
  • Wrong issuer or key: iss differs by a trailing slash or tenant, or the kid is not in the JWKS the API downloaded, which happens after key rotation if the key set is cached too long.

Where to store tokens

There is no universally right answer. An HttpOnly, Secure, SameSite cookie keeps the token away from JavaScript, so XSS cannot read it, but you then need CSRF protection. Memory or localStorage avoids CSRF but exposes the token to any script running on the page. Short lifetimes and refresh token rotation limit the damage in either case. For the same reason this page does not save the token you paste.

Frequently Asked Questions

Does decoding a JWT verify it?

No. Decoding only reverses the Base64URL encoding, and anyone can create a token with any payload. A token is trustworthy only after its signature has been checked with the issuer's key and its exp, nbf, iss and aud claims have been validated on the server.

Is it safe to paste a production token here?

The token is decoded with JavaScript in your browser and is not uploaded or stored. Even so, a live access token is a credential: prefer an expired one when you share screenshots, and never paste tokens into chat or tickets.

Why can't I verify an RS256 or ES256 token with a secret?

Those algorithms use a key pair. Verification needs the issuer's public key, usually published at a JWKS URL such as /.well-known/jwks.json, not a shared secret. The checker on this page covers the HMAC algorithms HS256, HS384 and HS512.

Why does my token have five parts?

It is a JWE, an encrypted token. Only the first segment, the protected header, is readable; the payload can be decrypted only by the intended recipient with its key.

Can I edit the payload and reuse the token?

Not if the server verifies signatures. Any change to the header or payload invalidates the signature, and a correctly configured server will return 401. If editing a token works, the server is not verifying it, and that is a security bug to fix.