Reading a JWT: What the Three Parts Actually Contain (and What They Do Not)

A JSON Web Token looks like random noise, but it is three Base64url segments doing three different jobs. This guide shows how to decode one by hand, what the header and payload claims really mean, why the signature is not encryption, and the verification mistakes that cause real vulnerabilities.

You paste a JWT into a decoder, three colored boxes appear, and you move on. But most people never learn what those three parts are or what the token can and cannot guarantee. That gap causes real bugs: trusting a payload nobody verified, forgetting an expiry, or treating the whole thing like a secret. Once you understand the structure, a JWT is completely legible — you can decode one in your head with nothing more than a text editor.

A JWT is three parts joined by dots

The format is header.payload.signature, and every part is Base64url-encoded (the URL-safe variant of Base64, with - and _ instead of + and /, and no padding). Here is a real, self-consistent example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkhlbiIsImFkbWluIjpmYWxzZSwiZXhwIjoxNzAwMDAwMDAwfQ
.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Split on the dots and decode each segment and you have the entire token in plain text. The first part decodes to {"alg":"HS256","typ":"JWT"} — the algorithm and type. The second decodes to the payload: a JSON object of claims like subject (sub), name, an admin flag, and an expiry (exp). The third is the signature, which is not decodable into anything meaningful on its own.

The header: what you are trusting

The header tells the verifier how to check the signature. The two fields that matter are alg (the signing algorithm, e.g. HS256 for HMAC-SHA256, or RS256 for RSA) and typ (almost always "JWT"). The algorithm choice is the crux of security: it determines whether the token is signed with a shared secret (HMAC, symmetric — the same key signs and verifies) or a key pair (RSA/ECDSA, asymmetric — the server signs with a private key and anyone can verify with the public key).

The payload: claims, not secrets

The payload is where your application data lives, and it is fully readable by anyone holding the token. There are a handful of registered claims with standard meanings:

  • sub — the subject, usually the user ID.
  • exp — expiry time in seconds since the epoch. A valid token must have exp in the future.
  • iat — issued-at time.
  • iss — issuer, who signed it.
  • aud — audience, who it is meant for.
  • nbf — not-before, the time after which it becomes valid.

Beyond these you add whatever your app needs — roles, permissions, a session ID. The critical point: because the payload is only encoded, not encrypted, you must never put anything secret in it. A JWT is signed to prove it was not tampered with; that is not the same as hiding it. Treat the payload as if it were printed on a postcard, because it effectively is.

Our JWT tool decodes the header and payload for you, pretty-prints the claims, and flags whether exp has already passed — which is the single most common thing you actually want to know when debugging a token that "should" work.

The signature: integrity, not confidentiality

The signature is a cryptographic hash of the first two parts, run through the header's algorithm with a key. For HS256 that is HMAC-SHA256(base64(header) + "." + base64(payload), secret). Anyone who changes the payload — flipping "admin": false to true — changes the input to the hash, so the signature no longer matches and a correct verifier rejects it.

This is what makes a JWT trustworthy without a database lookup: you do not need to remember issuing the token, you just recompute the signature and check it matches. But it only works if the secret is genuinely secret and the verifier actually performs the check. A token that is merely decoded is unverified — the payload could be anything an attacker typed.

The four mistakes that cause real vulnerabilities

  1. Trusting a decoded payload without verifying the signature. If you never check the signature, an attacker can forge any claims they like.
  2. Accepting alg from the token. Pin the algorithm server-side and reject mismatches; never honor "none".
  3. Ignoring exp and nbf. A token with no expiry check is a permanent credential. Always validate the time claims.
  4. Using a weak or leaked HMAC secret. HS256 is only as strong as the secret. If it is short, guessable, or shared across environments, the whole scheme collapses.

When to use a JWT at all

JWTs shine when you need stateless verification across services — a gateway signs once, many backends verify with the public key, no shared session store. They are a poor fit when you need instant revocation, because a valid signed token stays valid until it expires; you end up building a blocklist, which reintroduces the state you were trying to avoid. For a single server with a session store, a plain opaque session cookie is often simpler and safer.

If you just need to see what is inside a token you already have — to debug a login, check an expiry, or confirm a claim made it through — the decoder does it entirely in your browser. Paste it, read it, and nothing you inspect ever leaves your machine.

JWT DecoderInspect tokens safely — open the tool