JSON Web Tokens (JWTs) are everywhere — API authentication, single sign-on, microservices communication. If you build web applications, you'll work with them. This guide covers the structure, how they work, and the security practices that actually matter.
What Is a JWT?
A JWT is a compact, URL-safe token format defined by RFC 7519. It contains a JSON payload with claims (assertions about a user or entity) and is digitally signed so the receiver can verify it hasn't been tampered with.
A JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Three parts separated by dots: Header, Payload, and Signature.
The Three Parts
1. Header
The header specifies the signing algorithm and token type. It's Base64url-encoded JSON:
{
"alg": "HS256",
"typ": "JWT"
}
Common algorithms: HS256 (HMAC + SHA-256, symmetric), RS256 (RSA + SHA-256, asymmetric), ES256 (ECDSA + SHA-256, asymmetric).
2. Payload
The payload contains claims — key-value pairs with information about the user and token metadata:
{
"sub": "user-12345",
"name": "Jane Developer",
"email": "[email protected]",
"role": "admin",
"iat": 1711234567,
"exp": 1711238167
}
Registered claims (standard fields):
sub— Subject (user ID)iat— Issued at (Unix timestamp)exp— Expiration time (Unix timestamp)iss— Issuer (who created the token)aud— Audience (who the token is for)nbf— Not before (token not valid before this time)jti— JWT ID (unique identifier for the token)
You can add any custom claims you need (role, permissions, org_id, etc.).
3. Signature
The signature is created by hashing the encoded header and payload with a secret key:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
The signature lets the server verify that the token hasn't been modified. If someone changes a claim in the payload, the signature won't match and the token is rejected.
How JWT Authentication Works
- User logs in — Client sends credentials (username + password) to the server.
- Server creates JWT — After verifying credentials, the server creates a JWT with user claims, signs it, and returns it.
- Client stores JWT — The token is stored (usually in memory or an httpOnly cookie).
- Client sends JWT with requests — Each API request includes the token in the
Authorization: Bearer <token>header. - Server validates JWT — The server verifies the signature and checks expiration before processing the request.
HS256 vs RS256: Symmetric vs Asymmetric
HS256 (HMAC) uses a single shared secret for both signing and verification. Simple, fast, but every service that verifies tokens needs the secret — a security risk in distributed systems.
RS256 (RSA) uses a private key to sign and a public key to verify. Only the auth server needs the private key; all other services use the public key. Ideal for microservices architectures.
HS256: auth-server signs with SECRET → api-server verifies with SECRET
RS256: auth-server signs with PRIVATE_KEY → api-server verifies with PUBLIC_KEY
Security Best Practices
Token Storage
- Best: httpOnly, Secure, SameSite cookies — immune to XSS attacks
- Acceptable: In-memory (JavaScript variable) — lost on refresh but safe from XSS persistence
- Avoid:
localStorageorsessionStorage— accessible to any JavaScript on the page, including XSS payloads
Always Validate These
- Signature — Never trust an unverified token
expclaim — Reject expired tokensissclaim — Confirm the token came from your auth serveraudclaim — Confirm the token is meant for your servicealgheader — Reject"alg": "none"(a classic attack vector)
Token Expiration
Use short-lived access tokens (5–15 minutes) paired with longer-lived refresh tokens. This limits the damage window if a token is stolen.
Access token: exp = 15 minutes
Refresh token: exp = 7 days (stored in httpOnly cookie, rotated on use)
Token Revocation
JWTs are stateless — the server doesn't track them. To revoke a token before expiration, you need a blocklist (database or Redis set of revoked jti values). Check the blocklist on every request. This adds a DB lookup but is necessary for logout, password changes, and compromised accounts.
Common Mistakes
- Putting secrets in the payload — The payload is readable by anyone.
- Using
"alg": "none"— Some libraries accept unsigned tokens if the algorithm is set tonone. Always validate the algorithm server-side. - Not checking expiration — Tokens without
expnever expire. Always set and enforce expiration. - Long-lived tokens with no rotation — A stolen token that's valid for months is a serious risk.
- Storing tokens in localStorage — Vulnerable to XSS. Use httpOnly cookies instead.
When NOT to Use JWTs
- Session management for a monolith — Server-side sessions with a session ID cookie are simpler and easier to revoke.
- Storing large amounts of data — JWTs are sent with every request. Keep payloads small (under 1 KB).
- When you need instant revocation — If immediate logout is critical, server-side sessions are more straightforward.
Try It Yourself
Paste any JWT into the JWT Decoder tool to instantly see its header, payload, and expiration — all processed in your browser, no data sent anywhere.