If you have ever logged into a web app and inspected the network requests, you have probably seen a long token that starts with eyJ. That is a JWT, a JSON Web Token: the standard way APIs carry proof that you are logged in. Understanding its structure takes five minutes and saves you from a whole category of security mistakes.
Anatomy: three parts, two dots
A JWT has exactly three segments separated by dots: header.payload.signature. Each segment is Base64URL-encoded, which is Base64 with URL-safe characters and no padding. Decoding the first two segments is just Base64 decoding — no secret required:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 <- header
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkYSIsImlhdCI6MTUxNjIzOTAyMn0 <- payload
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c <- signatureThe header says which algorithm signed the token, usually HS256 or RS256. The payload carries claims, which are statements about the user and the token. The signature is what makes the token trustworthy — it is computed from the header, the payload, and a secret key that only the server knows.
What lives in the payload
Decoding the payload above gives you plain JSON with claims like these:
{
"sub": "1234567890",
"name": "Ada",
"iat": 1516239022,
"exp": 1516242622
}The standard claims are sub (subject, usually the user ID), iat (issued at), and exp (expiry), both as Unix timestamps in seconds. Custom claims like name or role are common too. If you need to read a payload like this, the same formatting rules from How to Format JSON in JavaScript apply — it is JSON all the way down.
Decoding is not verifying
This is the critical distinction. Anyone can decode a JWT and read its payload — that is by design. Decoding proves nothing. Verifying means recomputing the signature with the secret key and confirming the token was not tampered with. Only the server can do that. Never trust a decoded payload on the client: a user can edit their own token in seconds and give themselves admin: true. Your server must verify the signature on every request.
How to decode one safely
JWTs often contain user IDs, emails, or roles. Pasting a production token into a random website hands that data to a stranger, and some sites log everything pasted into them. The safe rule is simple: decode tokens locally, in your own browser, where the token never leaves your device. A local decoder can show you the header and payload instantly without sending anything anywhere.
Two more habits that prevent real incidents: never put secrets, passwords, or API keys in a payload — it is readable by anyone who holds the token. And check exp before trusting a token: if Date.now() / 1000 is past the exp value, the token is expired and should be rejected, even if its signature is valid. On the storage side, prefer httpOnly cookies over localStorage for tokens in browsers: any JavaScript on the page, including a compromised third-party script, can read localStorage, while httpOnly cookies are invisible to JavaScript entirely.
Try it
Paste a token into the JWT Decoder to see its header and payload decoded locally — nothing is uploaded. Since JWTs are Base64 under the hood, the URL & Base64 Encoder is handy when you need to encode or decode the individual segments by hand.