If you’ve built or debugged a login system, you’ve met the JWT — a compact token that travels between client and server to prove who someone is. Here’s how it works and how to read one safely.
The three parts
A JWT is three Base64URL strings joined by dots: header.payload.signature.
- Header — metadata, mainly the signing algorithm, e.g.
{"alg":"HS256","typ":"JWT"}. - Payload — the claims, a JSON object of data about the user and token.
- Signature — a cryptographic signature over the header and payload, created with a secret (or private key), used to detect tampering.
You can split and read the first two parts with a JWT decoder.
Common claims
The payload holds claims — some standardized:
sub— subject (usually the user ID).iss— issuer (who created the token).iat— issued-at time (a Unix timestamp).exp— expiry time (a Unix timestamp). After this, the token should be rejected.aud— intended audience.
Plus any custom claims your app adds (roles, email, etc.).
How JWTs are used for auth
In a typical stateless flow: you log in, the server returns a signed JWT, and your client sends it on every request (usually in an Authorization: Bearer … header). The server verifies the signature and checks exp — if valid, it trusts the claims without a database lookup. That statelessness is the main appeal.
Signed, not (usually) encrypted
Most JWTs are signed (JWS), not encrypted. Signing proves integrity and authenticity, but the payload is only Base64URL-encoded — anyone can read it. So:
- Never put secrets in the payload (passwords, API keys, private data).
- Encryption is a separate thing (JWE) and much less common.
Security rules that matter
- Always verify the signature server-side with your secret/public key. Decoding (like our tool does) is not verification.
- Check
exp. An expired token must be rejected. - HS256 vs. RS256: HS256 uses one shared secret; RS256 uses a private key to sign and a public key to verify — better when multiple services need to verify but not issue.
- Never paste a real secret key into an online tool. Decoding a token to inspect claims is fine; verifying requires the key and belongs in your own code.
Reading a token
To debug an auth issue — is the token expired? does it have the right role? — decode it and inspect the payload. The free JWT decoder shows the header and payload and converts exp to a readable time, all locally in your browser so the token is never uploaded.