What a JWT is, what's inside one, and what it does and doesn't prove
Log in to almost any modern app and a JWT is what your browser carries afterwards: a long string of letters, digits and two dots that the server accepts as proof of who you are. It is widely used and widely misunderstood โ mostly in the belief that it's secret. The JWT decoder shows what's inside one on your device; this guide explains what that means.
Three parts, two dots
A JSON Web Token (RFC 7519, 2015) is header.payload.signature, each part Base64url-encoded (What Base64 is, and what it is not). The header is JSON naming the signing algorithm ({"alg":"HS256","typ":"JWT"}). The payload is JSON of claims: who the token is about (sub), who issued it (iss), who it's for (aud), when it expires (exp), when it was issued (iat), plus whatever the application adds (roles, email, a user ID). The signature is computed over the first two parts with a key. Tokens are typically sent in an Authorization header or a cookie.
Readable, not encrypted
Base64 is an encoding, not encryption. Anyone holding a token can decode the header and payload and read every claim, which is why the payload must never contain passwords, card numbers or anything the user shouldn't see about themselves or others. There is an encrypted variant (JWE), used rarely; the common JWT (technically a JWS) is signed, not hidden. Decoding a token to inspect its claims and expiry is a normal debugging step and needs no key.
What the signature proves
The signature lets the server verify that the token was issued by someone holding the signing key and hasn't been altered since. With HS256 the key is a shared secret (HMAC โ the same construction as How authenticator app codes work: TOTP, explained); with RS256 or ES256 the issuer signs with a private key and anyone can verify with the public one, which is how a login service can issue tokens that many separate APIs check without sharing a secret. Change one character of the payload and the signature fails. What it does not prove: that the token hasn't been stolen. A JWT is a bearer token โ whoever presents it is treated as the subject until it expires โ which is why expiry times are short (minutes to hours) and refresh tokens exist.
The mistakes that make JWTs unsafe
- Not verifying the signature, or accepting "alg":"none". Libraries have had this bug; a server must only accept the algorithms it expects.
- Confusing algorithms: an RS256 verifier tricked into treating the public key as an HMAC secret. Pin the algorithm server-side.
- Weak HMAC secrets: a short secret can be brute-forced offline from a single token. Use a long random one.
- Long expiries with no revocation. A stolen token is valid until exp; there is no logout on the server unless you keep a blocklist.
- Sensitive claims in the payload โ readable by anyone who sees the token.
- Tokens in URLs โ they end up in logs, history and referrers.
Decoding one safely
Paste a token into an online decoder and you have shared a live credential with that website. Decoding is pure Base64 arithmetic and needs no server; a tool that does it in your browser (as the decoder here does) never sees the token, and that is the only kind to use for a token that is still valid. Read the exp claim (a Unix timestamp โ Unix time, explained) to see whether it has expired, check iss and aud match what you expect, and remember that a decoder shows the claims but cannot verify the signature without the key โ "decoded" is not "trusted". The Base64 tool shows the encoding step; the hash generator the HMAC family behind HS256.
Sources and further reading
The claims in this guide rest on these references, which were checked when the guide was last updated. Spotted an error? The contact page says how to report it.