What Is a JWT?
A JWT (JSON Web Token, pronounced “jot”) is a compact, signed piece of JSON that a server hands to a client to prove who the client is. It has three parts separated by dots: header.payload.signature. The header and payload are Base64URL-encoded JSON that anyone can read; the signature lets the server detect tampering. A JWT is not encrypted by default, so it should never carry secrets. It is defined in RFC 7519 and is the usual format for OAuth 2.0 access tokens and OpenID Connect ID tokens.
The three parts of a JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← header
.eyJzdWIiOiIxMjMiLCJuYW1lIjoiQWRhIiwiZXhwIjoxODMwMDAwMDAwfQ ← payload
.QXhGfMYl6bFtlYu3eGuBK_p-B0dNsl88071EHzb3Awo ← signatureDecoded, the header and payload are ordinary JSON:
// header
{ "alg": "HS256", "typ": "JWT" }
// payload
{ "sub": "123", "name": "Ada", "exp": 1830000000 }The header names the signing algorithm. The payload holds claims, statements about the user and the token. The signature is computed over the first two parts with a key only the issuer holds. Change one character of the payload and the signature no longer matches. You can split and read any token with the JWT Decoder, which runs in your browser.
Why the encoding is Base64URL
Tokens travel in HTTP headers, cookies and sometimes URLs, so they must be plain text without characters that need escaping. Base64URL is standard Base64 with - and _ in place of + and /, and with the trailing = padding dropped. Encoding is not protection: anyone can reverse it. Our guide to what Base64 is covers the mechanics, and the Base64 Encoder decodes a segment by hand if you want to see it.
Standard claims
| Claim | Name | Meaning |
|---|---|---|
iss | Issuer | Who created the token, often a URL |
sub | Subject | Who the token is about, usually a user ID |
aud | Audience | Which service the token is meant for |
exp | Expiration | After this time the token must be rejected |
nbf | Not before | Before this time the token must be rejected |
iat | Issued at | When the token was created |
jti | JWT ID | A unique ID, useful for blocking replay |
Times are Unix timestamps in seconds, not milliseconds. A common bug is comparing exp to JavaScript's Date.now(), which returns milliseconds, and concluding every token is valid for another fifty thousand years. The Unix Timestamp Converter turns a claim into a readable date.
Signing algorithms
| alg | Type | Who can verify |
|---|---|---|
| HS256 | HMAC with SHA-256 and a shared secret | Only services that hold the same secret |
| RS256 | RSA signature with SHA-256 | Anyone with the public key |
| ES256 | ECDSA on the P-256 curve | Anyone with the public key; smaller signatures |
| EdDSA | Ed25519 signature | Anyone with the public key; fast, modern |
With HMAC, every service that verifies tokens could also mint them, so it suits a single backend. Asymmetric algorithms let an identity provider sign with a private key while many APIs verify with the public key, usually fetched from a JWKS endpoint.
Decoding is not verifying
Reading a payload tells you what the token claims. Only verifying the signature tells you whether to believe it. A server must, at minimum:
- Verify the signature with the expected key.
- Accept only the algorithms it expects, set in server config, never taken from the token's own header.
- Reject expired tokens and check
nbf, allowing a minute or so of clock skew. - Check
issandaud, so a token issued for another service is refused.
Step two matters because of two well-known attacks. The first sends a token with "alg": "none" and no signature, hoping a library accepts it. The second, algorithm confusion, switches RS256 to HS256 so a naive library uses the public key as an HMAC secret. Current libraries block both when configured with an explicit algorithm list, as RFC 8725 recommends.
JWT vs session cookie
A classic session stores state on the server and gives the browser a random ID. A JWT carries the state inside the token, so any server with the key can check it without a database lookup. That is the main benefit, and also the main drawback: a JWT stays valid until it expires, and you cannot easily revoke one early. The common compromise is a short-lived access token, minutes rather than days, paired with a refresh token the server can revoke.
Where the browser keeps the token matters too. Anything in localStorage can be read by any script on the page, so a single cross-site scripting bug exposes it. An HttpOnly, Secure, SameSite cookie keeps the token away from scripts, at the cost of needing CSRF protection on state-changing requests.
Encrypted JWTs
The tokens described here are technically JWS, JSON Web Signature. When the payload itself must be hidden, there is JWE, JSON Web Encryption, which has five dot-separated parts instead of three. JWE is far less common; most systems keep sensitive data out of the token and look it up server-side instead.
Quick answers
- Can anyone read a JWT? Yes, unless it is a JWE. Treat the payload as public.
- Is it safe to paste a token into a website? Only one that decodes locally. A live token grants access until it expires.
- How long should a JWT last? Access tokens commonly last 5 to 60 minutes; use refresh tokens for longer sessions.
- How big can it get? Every claim adds bytes to every request. Keep payloads small; many servers cap headers at around 8KB.