Skip to content
2xKit

JWT Tokens Explained: What's Inside One (and Why You Shouldn't Put Secrets in the Payload)

Why anyone can read a JWT's contents without a key, and why that means the payload should never hold secrets.

Quick answer

A JWT (JSON Web Token) is three Base64URL-encoded, dot-separated segments — header, payload, and signature — and the payload is only encoded, not encrypted, meaning anyone can decode and read it without any key; only the signature is cryptographically protected, so it verifies the token wasn't tampered with, not that its contents are secret. Decode any JWT and inspect its parts with the JWT Decoder.

A JWT is a compact way to pass verified claims (statements about a user or session) between two parties, and it shows up constantly in modern authentication systems: log in once, get a JWT back, and send that token with every subsequent request instead of re-authenticating each time. A JWT is a single string made of three parts separated by dots: `header.payload.signature`.

The three parts, decoded

The header is a small JSON object stating the token type and the signing algorithm used, for example `{"alg":"HS256","typ":"JWT"}`. The payload is where the actual claims live, things like a user ID, an expiration timestamp, and any custom data the issuing server wants to attach, also just a JSON object. Both the header and payload are Base64URL-encoded (a URL-safe variant of Base64) and joined with dots, they are not encrypted in a standard JWT, which is the single most important fact about the format: anyone who intercepts a JWT can decode the header and payload instantly with nothing more than a text editor or the JWT Decoder.

The signature is the part that actually provides security. It's computed by taking the encoded header and payload, combining them with a secret key (or private key, for asymmetric algorithms) known only to the issuing server, and running that through a signing algorithm. Anyone can verify the signature is valid if they have the corresponding public key or shared secret, but they can't forge a new valid signature without it, that's what stops an attacker from editing the payload (say, changing a user role from "user" to "admin") and having the server accept the tampered token.

Why the payload must never hold secrets

Because Base64URL encoding is trivially reversible by design, not a security measure, putting a password, API key, or any other sensitive value in a JWT's payload is functionally the same as writing it in plain text and handing it to anyone who intercepts the token. The signature protects integrity (has this token been altered since it was issued) but does nothing to protect confidentiality (can anyone read what's inside it). If a token genuinely needs to carry confidential data, that requires a different, encrypted token format (JWE) rather than a standard signed JWT (JWS), or simply keeping sensitive data server-side and referencing it by an opaque ID instead.

What actually stops a JWT from being tampered with

If someone edits the payload of a JWT, even changing a single character, and re-encodes it without access to the signing key, the resulting signature won't match what the server computes when it re-verifies the token, and the server rejects it. This is the entire security model: the token is fully readable but not fully writable without the key. It's also why JWTs commonly include a short expiration claim, since a stolen but still-valid token remains usable by an attacker until it expires or is explicitly revoked, there's no way to "unsend" a JWT once it's been issued and shared.

Frequently asked questions