JSON Web Tokens (JWT) are one of the most common standards for authentication and passing information between parties in a compact way. Here you'll see how they're built, how they're signed, and what mistakes to avoid.
What is a JWT?
A JWT is a string made of three parts separated by dots (.):
- Header: algorithm and token type (e.g. HS256, JWT).
- Payload: claims or “data” (user, role, expiration, etc.).
- Signature: ensures the token has not been tampered with.
Each part is Base64URL-encoded. That’s why you can “decode” a JWT and read the header and payload without the secret; the signature is what protects integrity.
Structure of a real JWT
A typical example looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ikp1YW4gUMOpcmV6IiwiZXhwIjoxNzA2OTAwMDAwfQ.abc123signature
- First part (header):
{"alg":"HS256","typ":"JWT"}. - Second part (payload):
{"sub":"1234567890","name":"Juan Pérez","exp":1706900000}. - Third part: the HMAC-SHA256 signature of the previous two parts.
Don’t store passwords or highly sensitive data in the payload—anyone can decode it.
How the signature is generated
With HS256, the server uses a shared secret key and applies HMAC-SHA256 to base64(header).base64(payload). If someone changes a single byte in the payload, the signature no longer matches and the token is considered invalid.
With RS256, the server signs with the private key and anyone with the public key can verify. This is useful when multiple services need to validate the token without holding the secret.
Common security mistakes
- Storing secrets in the payload: the payload is readable; don’t put passwords or critical data without extra encryption.
- Not verifying the signature: always validate the signature and the algorithm (avoid "alg": "none" or algorithm switching).
- Not checking exp (expiration): tokens should have an expiration date and the backend must enforce it.
- Weak or leaked keys: with HS256, a short or leaked key allows forging tokens.
Summary
A JWT gives you a “signed blob” that the client can send on each request (e.g. in the Authorization header) and the server only needs to verify the signature and claims. It’s useful for APIs and stateless sessions, as long as you don’t store sensitive information in the payload and you follow good signing and expiration practices.
If you want to inspect a token without writing code, you can use our free tool to decode header and payload instantly.