What is a JSON Web Token?
A JSON Web Token (JWT, pronounced "jot") is a compact, URL-safe string that encodes a set of claims as a signed JSON object. It is defined in RFC 7519 and is the dominant format for stateless authentication and authorization in web APIs.
Instead of asking the server to look up a session ID in a database on every request, the client presents a JWT that the server can verify independently using a cryptographic signature. This makes JWTs particularly well-suited for microservice architectures where multiple services need to authenticate requests without sharing a session store.
The three-part structure
A JWT is three Base64URL-encoded strings joined by dots:
Header
Declares the token type (typ: "JWT") and the signing algorithm used (alg).
{
"alg": "HS256",
"typ": "JWT"
}Payload
The claims: facts about the user or session. Registered claims like sub (subject), iat (issued at), and exp (expiration) are standardized; you can add any custom claims you need.
{
"sub": "user_123",
"name": "Alice",
"role": "admin",
"iat": 1700000000,
"exp": 1700086400
}Signature
Computed over the encoded header and payload using the algorithm declared in the header. Verifying the signature proves the token has not been altered and (in the case of RS256/ES256) that it was issued by the expected party.
The authentication flow
Here is the standard pattern used by virtually every JWT-based API:
- 1The client sends credentials (username + password) to the auth endpoint.
- 2The server validates the credentials, creates a payload with the user's ID and roles, signs it, and returns the JWT.
- 3The client stores the token (typically in memory or a secure HTTP-only cookie) and sends it with every request as
Authorization: Bearer <token>. - 4The server verifies the signature, checks the
expclaim, reads the payload, and grants or denies access. No database lookup required.
Signing algorithms: HS256 vs RS256 vs ES256
HS256 (HMAC-SHA256)
Uses a single shared secret. Simple to set up. The problem: every service that needs to verify tokens must hold the secret, increasing the attack surface. Best for single-server apps or internal services.
RS256 (RSA-SHA256)
Uses a private key to sign and a public key to verify. Only the auth server needs the private key. Every other service can verify tokens using just the public key, which is safe to distribute. Preferred for multi-service systems.
ES256 (ECDSA-SHA256)
Same asymmetric model as RS256 but using elliptic-curve cryptography. Produces much shorter signatures (64 bytes vs 256+ bytes), making it attractive for high-throughput APIs or mobile clients where bandwidth matters.
Standard claims reference
| Claim | Name | Description |
|---|---|---|
| iss | Issuer | Who issued the token, e.g. https://auth.example.com |
| sub | Subject | Who the token is about, typically a user ID |
| aud | Audience | Who the token is intended for; servers should reject tokens with a mismatched aud |
| exp | Expiration | Unix timestamp after which the token must not be accepted |
| nbf | Not before | Unix timestamp before which the token must not be accepted |
| iat | Issued at | Unix timestamp of when the token was issued |
| jti | JWT ID | Unique identifier for this token; used to prevent replay attacks and enable deny-listing |
Security best practices
- ‣Always verify the signature server-side. Never trust a decoded payload without validating the signature first. Use a well-maintained library (jsonwebtoken, PyJWT, java-jwt) rather than decoding manually.
- ‣Validate the algorithm. Pin the expected algorithm in your verification code. The classic alg: "none" attack exploits libraries that accept tokens claiming no signature is required.
- ‣Set short expiry times. A stolen token is valid until it expires. 15 minutes for access tokens is a common default; use refresh tokens (stored in HTTP-only cookies) to re-issue them silently.
- ‣Do not store JWTs in localStorage. localStorage is accessible to any JavaScript on the page. A single XSS vulnerability can drain all tokens. Prefer HTTP-only, Secure, SameSite=Strict cookies.
- ‣Never put sensitive data in the payload. The payload is readable by anyone who holds the token. Passwords, SSNs, and card numbers must not appear there.
- ‣Validate the audience (aud) claim. If your system issues tokens for multiple services, each service should reject tokens whose aud does not include its own identifier.
JWT vs. server-side sessions
JWTs are not always the right tool. Here is when each approach makes sense:
Use JWTs when
- ‣Multiple independent services need to verify tokens
- ‣You want stateless servers (no shared session store)
- ‣Mobile or single-page apps need to call third-party APIs
- ‣You are implementing OAuth 2.0 / OpenID Connect
Use server sessions when
- ‣You need instant token revocation (e.g. account suspension)
- ‣You are building a traditional server-rendered web app
- ‣You prefer a single authoritative session store
- ‣Payload size is a concern and you want tiny cookies
Frequently asked questions
Is a JWT encrypted?
No. The header and payload are Base64URL-encoded, which is reversible without any key. Anyone who intercepts the token can read its contents. Never put passwords, credit card numbers, or other sensitive data in a JWT. If you need an unreadable payload, use JWE (JSON Web Encryption) instead.
What is the difference between HS256 and RS256?
HS256 (HMAC-SHA256) uses a single shared secret: the same key signs and verifies the token, so every service that verifies JWTs must hold the secret. RS256 (RSA-SHA256) uses a private/public key pair: only the auth server holds the private key (signs), while any downstream service can hold the public key (verifies). RS256 is the safer choice in multi-service architectures.
How do I revoke a JWT before it expires?
JWTs are stateless by design, so there is no built-in revocation. Common approaches: keep a server-side deny-list of revoked token IDs (jti claim), use very short expiry times combined with refresh tokens, or switch to opaque tokens (random strings looked up in a database) when revocation is critical.
What does "Bearer" mean in the Authorization header?
"Bearer" is an HTTP authentication scheme defined in RFC 6750. It signals that whoever bears (holds) this token is allowed access. The full header looks like: Authorization: Bearer <token>. The word "bearer" itself carries no cryptographic meaning.
Can I decode a JWT without the secret?
Yes. The header and payload are just Base64URL-encoded JSON. You can decode them without any key. What you cannot do without the key is verify that the signature is valid, i.e., that the token has not been tampered with. Use our JWT Decoder to inspect any token instantly.
What is the "exp" claim and what happens when it passes?
The "exp" (expiration time) claim is a Unix timestamp indicating when the token must no longer be accepted. Any conforming server-side library will reject tokens whose exp is in the past. Choosing expiry duration involves a trade-off: shorter expiry (minutes) limits exposure if stolen, but requires refresh tokens to avoid forcing users to re-log in.