Skip to main content
CuteJSON
↑↓ Navigate Enter Select Esc Close

How JWTs work: header, payload, signature

Published 3 min read By Akash Kinkar Pandey

A JSON Web Token, RFC 7519, is a compact way to carry a set of claims from one party to another so the receiving side can trust who sent them, or at least check that nothing changed in transit. Every JWT you will ever see in an Authorization: Bearer header is the same three pieces joined by dots: header.payload.signature.

The three parts

The header and the payload are each a JSON object, base64url encoded. The header names the signing algorithm and the token type, typically something as small as {"alg":"HS256","typ":"JWT"}. The payload holds the claims: the data the token is actually carrying, plus a handful of registered names with defined meanings, iss (issuer), sub (subject), aud (audience), iat (issued at), nbf (not before), exp (expiry) and jti (a token id). None of that is required by the spec beyond what an issuer chooses to include, which is why two JWTs from two different services can carry completely different claims.

The signature is not JSON at all. It is the output of running the header and payload, still in their encoded form, through the algorithm named in the header, HS256 (HMAC with a shared secret) and RS256/ES256 (RSA or ECDSA with a private key) being the ones you will meet most often. Base64url decoding the signature gets you raw bytes with no further structure to read.

Encoded is not encrypted

Base64url is an encoding, not a cipher. Anyone holding a JWT can decode the header and payload with nothing more than a text editor, which is exactly what the JWT Decoder does. That means a JWT is never the right place for anything you would not put in a URL: a password, a credit card number, a full name someone has not agreed to expose. If a payload needs to stay confidential rather than merely tamper evident, that calls for a JSON Web Encryption token, a different and less common format, not a signed JWT with a stronger algorithm.

Decoding tells you nothing about trust

This is the part decoders get blamed for the most, so it is worth stating plainly: reading the claims out of a JWT proves nothing about whether the token is genuine. Verifying a signature means recomputing it with the actual secret or public key the token was signed with and checking it matches, byte for byte. A decoder that only splits the token on its dots and base64url decodes two of the three parts, which is what every pure client side decoder does, has no access to that key and cannot do that check. It can tell you what a token claims. It cannot tell you whether to believe it.

A well formed, cleanly decoding token can still be forged, expired, or signed with a key that was never meant to issue it. exp and nbf are worth checking on every decode for exactly this reason: a token past its expiry, or not yet valid, is invalid regardless of whether its signature checks out, and reading those two fields as the timestamps they represent rather than raw numbers is the fastest way to catch it at a glance.

What this is useful for

Despite that limit, decoding without verifying has real, everyday uses: reading which scopes an access token was issued with while debugging an API integration, checking whether the token your app just received actually expired (rather than guessing from a vague 401), or inspecting what a third party’s token contains before deciding whether your service needs to trust it at all. None of that requires the signing key, only the token itself.

Paste one into the JWT Decoder and it separates the header, the registered claims and the remaining payload into their own rows, flags an expired or not-yet-valid token immediately, and shows the raw signature bytes without pretending to have checked them against anything.

Back to all posts