What a JWT actually is
A JWT (JSON Web Token, pronounced "jot") is a compact, URL-safe string used to carry claims between two parties — typically an authentication server and an API. It is made up of three parts separated by dots:
header.payload.signature
Each part before the dots is base64url-encoded JSON (base64url is the URL-safe variant of base64: + becomes -, / becomes _, and trailing = padding is dropped). A real token looks like one long opaque string:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Decoding simply reverses the base64url encoding so you can read the JSON inside. It does not require any secret or key — anyone can do it.
How to decode each part
To decode a JWT by hand, split it on the two dots and base64url-decode the first two segments:
- Header — the first segment. Describes the token type and the signing algorithm, e.g.
algandtyp. - Payload — the second segment. Holds the claims: the actual data about the subject and the token.
- Signature — the third segment. This is not JSON and is not meant to be read. It is a cryptographic value used to verify the first two parts.
The fastest way is to paste the whole token into Formatly's JWT Decoder. It splits the token, base64url-decodes the header and payload, and pretty-prints the JSON for you — all locally in your browser. The token is never uploaded or sent anywhere.
A worked example
Take the token above and decode its first two segments. The header base64url-decodes to:
{
"alg": "HS256",
"typ": "JWT"
}
This tells you the token was signed with HMAC-SHA256. The payload decodes to:
{
"sub": "1234567890",
"name": "Jane Doe",
"iat": 1516239022,
"exp": 1516242622
}
Now the claims are human-readable: a subject ID, a name, an issued-at time, and an expiry. The signature segment stays encoded — it only matters during verification.
Common claims and reading expiry
The JWT spec defines a set of registered claims. The ones you'll see most often are:
sub(subject) — who the token is about, usually a user ID.iss(issuer) — who created and signed the token.aud(audience) — the intended recipient(s); your API should reject tokens whoseaudisn't you.iat(issued at) — when the token was created.exp(expiration) — after this time the token must be rejected.nbf(not before) — the token is invalid until this time.
The time-based claims (iat, exp, nbf) are NumericDate values — seconds since the Unix epoch (1970-01-01 UTC), not milliseconds. To check expiry, compare exp against the current epoch time. In the example, exp is 1516242622, exactly one hour after iat (1516239022), so the token had a one-hour lifetime. Formatly's decoder surfaces these timestamps so you can see at a glance whether a token is expired.
Decoding is NOT verifying
This is the single most important thing to understand. Decoding only reads the data; it proves nothing. Anyone can take a token, change the payload, re-encode it, and produce a string that decodes perfectly — but the signature won't match.
- Never trust an unverified token. The decoded payload is just claims someone says are true. Your server must verify before acting on them.
- The signature proves integrity. Verification recomputes the signature over the header and payload using the secret (for HMAC like
HS256) or the issuer's public key (for RSA/ECDSA likeRS256). If it matches, the token wasn't tampered with and came from someone holding the key. - A decoder like Formatly's does not — and cannot, without the key — tell you a token is genuine. It just shows you what's inside.
Signed, not encrypted
A standard JWT is signed, not encrypted. The payload is only base64url-encoded, which is trivially reversible — exactly what we did above. That means:
- Never put secrets in the payload. Passwords, API keys, private personal data, or anything confidential is effectively public to anyone who holds the token.
- Treat the payload as readable by anyone who can intercept the token. If you genuinely need confidentiality, that's a separate standard (JWE), not a plain JWT.
Because tokens often live in logs, browser storage, and request headers, assume the contents will be seen.
Decode safely with Formatly
Pasting a token into a random online decoder is risky — many real tokens are live credentials. Formatly's JWT Decoder runs entirely in your browser. Nothing is uploaded, logged, or transmitted; the decoding happens on your machine, so even a production token stays private. Paste your token, read the header and payload, check exp — then close the tab and it's gone.