DevTools Hub
All guides

What's actually inside a JWT, and why it isn't encrypted

A JWT is three Base64url segments anyone can read. What each part holds, what the signature does and doesn't prove, and the mistakes that follow.

18 August 20262 min read

The most consequential misunderstanding about JSON Web Tokens is that they are encrypted. They are not. A JWT is signed, which is a different guarantee entirely, and confusing the two leads directly to leaking data you assumed was hidden.

Three segments, separated by dots

The header says which algorithm signed the token and, often, which key was used. The payload holds the claims — who the token is about, who issued it, when it expires, and whatever else the issuing system chose to include. The signature is computed over the first two segments using a secret or private key.

The header and payload are Base64url encoded, not encrypted. Anyone holding the token can decode them instantly, without any key, which is exactly what a decoder does. Encoding is for safe transport, not secrecy.

What the signature proves

The signature proves the token has not been altered since it was issued, and that it was issued by someone holding the key. That is all. It does not hide the contents, it does not prove the token is still valid, and it does not prove the token has not been stolen and replayed by someone else.

Which is why alg: none is dangerous. It declares the token unsigned, and any system that accepts such a token is trusting data an attacker can freely rewrite. Reject it explicitly rather than relying on a library's defaults.

Expiry, and what it doesn't cover

exp is a Unix timestamp in seconds, and a token past it must be rejected. nbf does the reverse, marking a token as not yet usable. Both are worth checking explicitly rather than assuming the library did it.

Neither covers revocation. A token issued for a week remains cryptographically valid for a week even if the user logs out, changes their password, or is deleted. If you need to revoke early, you need server-side state — a deny list or short-lived tokens with refresh — because the token itself cannot tell you.

In short

Never put anything sensitive in a JWT payload; assume it is public. Check expiry yourself, reject alg: none explicitly, and remember that a valid signature says nothing about whether the token should still be honoured.

JWT Decoder

Decode a JSON Web Token, read its claims, check expiry, and verify HS, RS or ES signatures — without the token ever leaving your browser.

Open the tool

Keep reading