Blog

What Is Inside a JWT?

2 September 2026

A JSON Web Token looks like line noise with two full stops in it, and it is carrying the answer to "who is this user?" for a large share of the internet. The single most important thing to know about one is also the most commonly misunderstood: it is signed, not encrypted. Anyone holding a JWT can read everything in it.

Three parts, two dots

Every JWT is three base64url-encoded segments joined by dots: header.payload.signature.

The header says how the token was signed — {"alg":"HS256","typ":"JWT"}. The payload carries the claims: who the token is about, when it expires, what it permits. The signature is a cryptographic checksum over the first two parts, computed with a secret only the issuer holds.

Base64url is a small variation on ordinary Base64: - and _ replace + and /, and the trailing = padding is dropped, so a token survives being put in a URL unescaped.

Encoded is not encrypted

Base64 is not a cipher. It has no key, and reversing it is a two-line function. Paste any JWT into our decoder — no secret required — and the payload is right there, because that is how the format is designed to work.

What the signature buys is integrity, not secrecy. It proves the token was issued by someone holding the secret and has not been altered since. Change one character of the payload and the signature stops matching.

The practical consequence: never put anything confidential in a payload. Not a password, not a card number, not an internal note about the user. Assume the contents are public to anyone who ever holds the token, because they are.

The claims worth knowing

RFC 7519 registers a handful of standard claim names. sub is the subject — usually the user ID. iss is the issuer that minted the token, and aud the audience it was meant for; a server should reject a token addressed to a different audience even if the signature is perfect.

The three time claims — exp, nbf and iat — are NumericDates: seconds since the Unix epoch, not milliseconds. Passing one straight to a JavaScript Date without multiplying by a thousand produces a date in January 1970, which is the single most common JWT bug after assuming encryption.

The alg:none attack

The famous early JWT vulnerability, and a lesson that generalises. The specification allows alg to be none, meaning an unsigned token — and a number of libraries once trusted the header to say which algorithm to verify with.

So an attacker could take a legitimate token, edit the payload to claim to be somebody else, set alg to none, delete the signature entirely, and be accepted. The token told the server not to check it, and the server obliged.

The same shape appears in the RS256/HS256 confusion: if a server accepts either, an attacker can take the issuer's public key — which is public — and use it as an HMAC secret to sign a forged token. The defence in both cases is the same: the server decides which algorithm is acceptable, and the token does not get a vote.

Why expiry is short, and what that costs

A JWT is validated by checking a signature, not by looking anything up. That is the entire appeal — any server with the key can verify a token without a database round trip, which is what makes JWTs attractive for distributed systems.

It is also the drawback: there is nothing to consult, so there is nothing to revoke. Log a user out, delete their account, change their permissions — an already-issued token keeps working until exp passes. That is why access tokens are usually given lifetimes of minutes and paired with a long-lived refresh token that is checked against a database. Short expiry is the compromise between statelessness and the ability to say no.

FAQ

Is a JWT encrypted?

No. A standard JWT is signed, not encrypted. The header and payload are base64url-encoded, which is an encoding rather than a cipher — anyone holding the token can decode and read them without any key at all. The signature only proves the token was issued by someone with the secret and has not been altered since. Never put a password, a card number or anything else confidential in a payload. The separate JWE standard does provide encryption, but it is a different format and far less commonly used.

Can I decode a JWT without the secret?

Yes, and that is by design. Decoding needs no key because the payload is only base64url-encoded. The secret is required for one thing: checking that the signature matches, which proves the token is genuine and unmodified. So a tool can show you everything inside a token it cannot verify, and being able to read a JWT is never itself evidence that anything has been compromised.

What do the claims exp, iat, nbf, sub and iss mean?

They are the registered claims from RFC 7519. exp is the expiry time and nbf the time before which the token must be rejected; iat records when it was issued. sub identifies the subject, usually the user the token is about, and iss names the issuer that created it. aud states the intended audience, and a server should reject a token addressed to somebody else. All three time claims are NumericDate values: seconds since the Unix epoch, not milliseconds.

What is the alg:none attack?

An early and famous JWT vulnerability. The specification permits an alg value of none, meaning an unsigned token, and several libraries once honoured whatever the header requested. An attacker could take a valid token, change the payload, set alg to none, strip the signature and be accepted as any user. The fix is for the server to decide which algorithm it accepts rather than trusting the token to say — the same reasoning applies to a server that accepts both HS256 and RS256, where an attacker can sign with the public key as if it were an HMAC secret.

Related guides

Try it now: Free JWT Decoder

Tools from this guide

JWT Decoder Read a token’s header, payload and expiry — and verify HS256.