JWT explained: what a JSON Web Token is and how it works
What a JSON Web Token is, how the header.payload.signature structure works, which claims and algorithms exist, and how to validate JWTs safely.
On this page
- Anatomy: three parts, two dots
- The payload is not secret
- The seven registered claims
- Which algorithms sign the token
- How a JWT should be validated
- Where JWTs are used
- Security checklist
- FAQ
- Is the content of a JWT secret?
- What is the difference between HS256 and RS256?
- What happens when a JWT expires?
- Why does the server reject a JWT with an unknown algorithm?
- Summary
A JSON Web Token (JWT, pronounced like the word "jot") is a compact, URL-safe string that carries a set of claims — statements about a subject — between two parties, signed so the receiver can verify who wrote it. In practice you meet JWTs as the tokens your API returns after login: the client sends one back on every request, and the server accepts it because the signature proves it was issued by someone holding the right key.
Anatomy: three parts, two dots
A signed JWT uses the JWS Compact Serialization: three base64url-encoded parts joined by periods, in the order header.payload.signature. Base64url encoding is like base64 but uses - and _ instead of + and /, and omits the padding = — which is what keeps the token safe to put in HTTP headers and URLs.
<header>.<payload>.<signature>
| Part | What it contains | Example (decoded) |
|---|---|---|
| Header | The algorithm (alg) used to sign, plus an optional type (typ) |
{"alg":"HS256","typ":"JWT"} |
| Payload | The claims — the actual data the token asserts | {"iss":"debod-id","sub":"user-42","exp":1787091072} |
| Signature | The result of signing the encoded header + payload with the key | 43-char base64url value (HS256) |
A real token looks like the example in RFC 7519 itself (sections abbreviated with ... as in the RFC, and one line break shown for display only):
eyJ0eX...NiJ9.eyJpc3...eGFt
cGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
The signature is computed over the encoded header and payload — the two strings joined with a . — not over the decoded JSON. Change one byte of the payload and every future signature check fails.
The payload is not secret
Base64url is encoding, not encryption. Anyone who can read the token can decode the payload and see every claim in plain text. The RFC is explicit on this: a JWT may contain privacy-sensitive information, and when it does, measures must be taken to prevent its disclosure. That is why sensitive data (passwords, personal identifiers) must never go into a plain signed JWT — use encrypted JWTs (JWE) for that.
The seven registered claims
The JWT specification defines seven standard claim names. None of them are mandatory in every case; applications decide which they use.
| Claim | Name | Meaning |
|---|---|---|
iss |
Issuer | The principal that issued the token |
sub |
Subject | The principal the claims are about |
aud |
Audience | The recipients the token is intended for |
exp |
Expiration time | Time on or after which the token must not be accepted |
nbf |
Not before | Time before which the token must not be accepted |
iat |
Issued at | When the token was issued |
jti |
JWT ID | Unique identifier, usable to prevent replay |
Two of these drive most real-world behavior. exp and nbf are timestamps as NumericDate values: seconds since 1970-01-01 00:00:00 UTC, ignoring leap seconds. aud is strict — a recipient that does not identify itself with a value in the audience claim must reject the token.
Which algorithms sign the token
The alg value in the header names the signing algorithm. The three you will meet most are defined in RFC 7518:
alg |
Algorithm | Key requirement |
|---|---|---|
HS256 |
HMAC SHA-256 | Shared secret, at least 256 bits |
RS256 |
RSASSA-PKCS1-v1_5 with SHA-256 | RSA private/public key pair, at least 2048 bits |
ES256 |
ECDSA with P-256 and SHA-256 | EC key pair |
HS256 is symmetric: the same secret both signs and verifies, so only one party (usually the server) should hold it. RS256 and ES256 are asymmetric: a private key signs, any holder of the matching public key verifies — which is why they fit multi-service setups where several services must check the same issuer's tokens without sharing a secret.
How a JWT should be validated
RFC 7519 defines the validation steps formally; the essentials for an application:
- Check the token has at least one
.— split into header, payload, signature. - Decode and parse the header as valid JSON; confirm you understand and support the
algvalue. - Recompute the signature over the encoded header and payload with the correct key for
alg, and compare — treat the token as invalid if they differ. - Decode and parse the payload as valid JSON.
- Enforce the registered claims you care about:
exp(reject expired),nbf(reject early),aud(reject if you are not an intended audience). - Reject if the algorithm used is not one your application explicitly allows.
The last point is a security requirement, not a suggestion: which algorithms are acceptable is an application decision, and a token must be rejected unless the algorithms used in it are acceptable. The classic failure is accepting alg: none (an unsecured JWT with an empty signature) or swapping RS256 for HS256 to make the server verify an asymmetric signature with the public key as a shared secret. Library flaws of exactly this kind have been patched repeatedly — and new alg: none vulnerabilities still surface years later — so verification must never be driven by the header alone.
Where JWTs are used
- API authentication — after login the server issues a token, the client sends it in the
Authorization: Bearer <token>header, and the server trusts the signature. This is stateless: the server does not have to store session state, because the token is self-contained. - Single sign-on — the token format was designed for browser SSO contexts, letting an identity provider hand a verified identity to a service provider.
- OAuth-style client assertions — a client can present a signed JWT to a token endpoint and receive an access token in return.
Security checklist
- Store tokens in an
HttpOnlycookie, not inlocalStorageorsessionStorage— client-side JavaScript (including browser extensions) can read those storage areas, which exposes the token to any XSS bug. - Never let the header alone drive verification: enforce an algorithm allowlist on the server.
- Use a key of appropriate size for the algorithm (256 bits minimum for HS256, 2048 bits for RSA).
- Keep
expshort and allow only a little leeway (the RFC suggests no more than a few minutes) for clock skew. - Never put sensitive data in a plain signed JWT payload — it is readable by anyone.
- Use
jtiwhen you need to stop a token from being replayed.
FAQ
Is the content of a JWT secret?
No. A JWT is signed, not encrypted: the header and payload are only base64url-encoded, so anyone who can read the token can decode and see every claim in plain text. That is why sensitive data like passwords must never go into a plain signed JWT — for that, use encrypted JWTs (JWE). What makes the token trustworthy is its signature, not the secrecy of its contents.
What is the difference between HS256 and RS256?
HS256 is a symmetric algorithm: the same secret key signs and verifies, so only one party (usually the server) should hold it. RS256 is asymmetric: a private key signs, and anyone with the matching public key verifies — which is why it fits multi-service setups where several services must check the same issuer's tokens without sharing a secret. Both are defined in RFC 7518.
What happens when a JWT expires?
The exp (expiration time) claim states the time on or after which the token must not be accepted. Validation must reject a token past exp — and also reject tokens used before the nbf (not before) claim and recipients not identified in the aud (audience) claim. That is why the server checks exp on every JWT request: an expired token is treated as invalid.
Why does the server reject a JWT with an unknown algorithm?
Because which algorithms are acceptable is an application decision — a token must be rejected unless the algorithm it uses is on the allowlist. The classic failure is accepting alg: none (an unsigned JWT) or swapping RS256 for HS256 so the server verifies an asymmetric signature using the public key as a shared secret. Verification must never be driven by the header alone: check the signature, check the claims, then check the algorithm against the allowlist.
Summary
A JWT is a signed, compact container for claims: three base64url parts (header, payload, signature), seven standard claims, and a handful of common algorithms. It is not encryption — the payload is readable by anyone — and it is only as trustworthy as your validation: check the signature, check the claims, and check the algorithm against an allowlist. Used that way, JWTs give you stateless, verifiable authentication without a session store.
Sources
- IETF RFC 7519 — JSON Web Token (JWT) (rfc-editor.org/rfc/rfc7519)
- IETF RFC 7518 — JSON Web Algorithms (JWA) (rfc-editor.org/rfc/rfc7518)
- Wikipedia — JSON Web Token (en.wikipedia.org/wiki/JSON_Web_Token)
Share this guide
Related guides
- How to add JWT authentication to a Node.js API: a practical guide
Step-by-step implementation of JWT authentication on a Node.js/Express API: password hashing, login endpoint, token verification middleware, refresh tokens, and the common security pitfalls.
- What is OAuth 2.0? How OAuth works and how to secure it
OAuth 2.0 is an open standard for delegated authorization defined in RFC 6749. It lets a user (the resource owner) grant a third-party application (the client) limited access to resources on another server without sharing their password. This guide explains the four roles, the main grant types, the recommended authorization-code flow with PKCE, and the security best practices from RFC 9700.
- What is authentication? Passwords, biometrics, SSO, and modern methods explained
Authentication is the process of verifying who you are. Learn how passwords, multi-factor authentication, biometrics, SSO, OAuth, and password managers work — and which methods offer the best protection for your application.