How JWT tokens work
Understand JSON Web Tokens: the header, payload and signature, standard claims like exp and iat, how signing works, and why decoding a JWT is not verifying it.
What a JWT is
A JSON Web Token (JWT, often pronounced jot) is a compact, URL-safe way to pass a set of claims between two parties. Claims are statements such as this user's ID is 42 or this token expires at 15:00. JWTs are widely used for login sessions, API access tokens and single sign-on, including the ID tokens in OpenID Connect. The format is defined in RFC 7519.
The key property is that a signed JWT can be checked for tampering by anyone with the right key, so a server can trust the claims without looking them up in a database on every request.
The three parts
A JWT is three Base64url-encoded strings joined by dots: header.payload.signature. A shortened example looks like eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.abc123... Because the first two parts are Base64url-encoded JSON, they almost always start with eyJ, which is how {" encodes.
Header
The header describes the token. The alg field names the signing algorithm, such as HS256 or RS256. typ is usually JWT. A kid (key ID) field tells the receiver which key to use when a service rotates several keys. Example: {"alg": "RS256", "typ": "JWT", "kid": "2026-10"}.
Payload
The payload holds the claims. RFC 7519 defines a set of registered claims, all optional:
- iss (issuer): who created the token.
- sub (subject): who the token is about, usually a user ID.
- aud (audience): who the token is intended for.
- exp (expiration time): after this moment the token must be rejected.
- nbf (not before): before this moment the token must be rejected.
- iat (issued at): when the token was created.
- jti (JWT ID): a unique ID, useful for preventing reuse.
The time claims are Unix timestamps in seconds, not milliseconds. Applications add their own claims too, such as roles, scopes or an email address.
Signature
The signature is computed over the encoded header and payload, joined by a dot, using the algorithm in the header. With HS256, an HMAC using SHA-256, the same shared secret signs and verifies. With RS256 (RSA) or ES256 (ECDSA), a private key signs and a public key verifies, so many services can check tokens while only the issuer can create them. If anyone changes a single character of the header or payload, the signature no longer matches.
Signed is not encrypted
The header and payload of a normal JWT are only encoded, not encrypted. Anyone who has the token can decode and read them, with no key. Do not put passwords, secrets or sensitive personal data in a JWT payload. If the contents must stay confidential, use an encrypted token (JWE), which has five parts instead of three, or keep the data on the server.
Decoding is not verifying
This is the most important point about JWTs. Decoding a token tells you what it claims. Verifying it tells you whether those claims can be trusted. A decoded payload that says admin: true proves nothing on its own, because anyone can create a token with any payload. Only a valid signature, checked with the correct key, shows that the token came from the issuer and has not been changed.
A server that accepts a JWT should, using a well-maintained library:
- Verify the signature with the expected key.
- Accept only the algorithms it expects, rather than trusting the alg in the header. Rejecting alg none and algorithm confusion attacks depends on this.
- Check exp and nbf against the current time, allowing a small clock skew.
- Check iss and aud match the expected issuer and audience.
- Apply any application rules, such as revocation lists or required scopes.
How to decode a JWT
- Open the JWT decoder.
- Paste the full token, all three parts including the dots.
- Read the decoded header and payload as formatted JSON.
- Check the Claims section: iat, nbf and exp are converted to readable ISO 8601 dates, and the tool shows whether the token is expired according to your device's clock.
Our decoder does not verify signatures. It decodes the header and payload only and labels the signature as not verified. Use it to inspect and debug tokens, not to decide whether a token is genuine. It also expects exactly three parts, so encrypted JWE tokens cannot be decoded.
JWT decoderRead the header, payload and expiry of a JWT locally. Signatures are not verified.How tokens are sent and stored
Clients usually send an access token in the HTTP Authorization header, as Authorization: Bearer followed by the token. Browser apps store tokens in memory, in cookies or in web storage. HttpOnly cookies cannot be read by page scripts, which limits the damage from cross-site scripting, while tokens in localStorage are easier to steal if a script is injected.
Because a signed JWT stays valid until it expires, it cannot easily be revoked. Common practice is to keep access tokens short-lived, often minutes rather than days, and to issue a separate refresh token that the server can revoke and use to obtain new access tokens.
Debugging common JWT problems
- Token rejected as expired: decode it and compare exp with the current time. Clock differences between servers are a frequent cause.
- Invalid audience or issuer: compare aud and iss in the payload with what the API expects.
- Invalid signature: the token may have been signed with a different key or environment, such as a staging token sent to production. Check kid and alg in the header.
- Dates look 50,000 years in the future: something set exp in milliseconds instead of seconds.
Handle tokens like passwords
A valid access token usually grants access to whatever its owner can reach, until it expires. Keep tokens out of logs, URLs, screenshots and support tickets. Our decoder runs entirely in your browser and does not upload the token, but it is still safer to use expired or test tokens when you share debugging steps with others.