Guide

JWT & Web Tokens Explained

What a JWT is, its three parts, standard claims, signing algorithms (HS256 vs RS256), verification pitfalls, revocation, and security best practices.

Last updated: 26 July 2026

A JWT (JSON Web Token, pronounced “jot”) is a compact, URL-safe way to represent claims — small statements like “this user is logged in and their ID is 42” — that can be verified and trusted because they are cryptographically signed. JWTs are the backbone of modern stateless authentication: an issuer signs a token, hands it to a client, and any service holding the right key can verify it without a database lookup. They are defined by RFC 7519.

The three parts

A JWT is three base64url-encoded segments joined by dots:

header.payload.signature

A real token looks like this (line-wrapped for readability):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTYiLCJuYW1lIjoiQWRhIiwiaWF0IjoxNzAwMDAwMDAwfQ.
dQw4w9WgXcQ_signature_bytes_here

The critical thing to understand: the payload is not encrypted, only signed. Anyone can copy the middle segment and base64url-decode it to read every claim. The signature proves the token was not modified and came from a holder of the key — it does not hide the contents. Never put secrets, passwords, or sensitive personal data in a JWT.

Base64url-decoding the first segment gives JSON describing how the token is signed:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

The second segment holds the claims — the actual data:

{
  "iss": "https://auth.example.com",
  "sub": "123456",
  "aud": "https://api.example.com",
  "exp": 1700003600,
  "iat": 1700000000,
  "name": "Ada Lovelace",
  "role": "admin"
}

Claims fall into three groups: registered (standardised names like those above), public (collision-resistant custom names), and private (agreed between two parties, e.g. role).

Signature

The signature is computed over the encoded header and payload:

HMAC-SHA256(
  base64url(header) + "." + base64url(payload),
  secret
)

For asymmetric algorithms the HMAC is replaced by a private-key signature. This mechanism — signing a base64url header and payload — is standardised separately as JWS (RFC 7515).

Standard (registered) claims

Claim Name Meaning
iss Issuer Who created and signed the token
sub Subject Who the token is about (usually the user ID)
aud Audience Who the token is intended for; verifiers must reject tokens not addressed to them
exp Expiration Reject the token at or after this Unix time
iat Issued At When the token was created
nbf Not Before Reject the token before this Unix time
jti JWT ID Unique identifier, useful for denylists and replay protection

All time claims are NumericDate values — seconds since the Unix epoch.

Signing algorithms

The alg header names the algorithm. The two families you will meet in practice:

Algorithm Family Key model Who can verify Typical use
HS256 / HS384 / HS512 HMAC + SHA-2 One shared secret Anyone with the secret (who can also sign) Single trusted backend issuing and verifying its own tokens
RS256 / RS384 / RS512 RSA signature Private key signs, public key verifies Anyone with the public key (cannot sign) OAuth/OIDC, multi-service or third-party verification
ES256 / ES384 / ES512 ECDSA signature Private key signs, public key verifies Anyone with the public key Same as RS256 but smaller keys/signatures

How verification works

To trust a JWT, a verifier must:

  1. Split the token and base64url-decode the header and payload.
  2. Recompute the signature over header.payload using the expected key and the algorithm you expect — not blindly the one named in the header.
  3. Constant-time compare it with the token’s signature.
  4. Check the claims: exp is in the future, nbf/iat are not in the future, iss and aud match what you expect.

Only if all of these pass is the token valid. A valid signature with a stale exp, or an aud meant for another service, must still be rejected.

Two dangerous attacks

The golden rule: the verifier, not the incoming token, chooses the acceptable algorithm(s) and key.

Access vs refresh tokens

Because a signed JWT is valid until it expires and needs no server lookup, you face a tension between convenience and control. The standard answer is two tokens:

The revocation problem

Statelessness is the whole appeal of JWTs — and also their biggest weakness. A leaked access token stays valid until exp no matter what. Mitigations, roughly in order of statefulness:

HMAC signatures & webhook verification

The same HMAC primitive behind HS256 secures webhooks. When GitHub, Stripe or Slack calls your endpoint, they compute an HMAC-SHA256 of the raw request body using a secret you both share, and send it in a header (e.g. X-Hub-Signature-256 for GitHub, Stripe-Signature for Stripe). Your server recomputes the HMAC over the exact raw bytes and compares:

It is the same idea as a JWT signature — a shared secret proving authenticity and integrity — just applied to an incoming HTTP request instead of a token.

Best practices

Standards & references

Try it

Decode a token or compute an HMAC signature entirely in your browser (nothing is uploaded) with the CodingSetu JWT and HMAC tools linked below.

Frequently asked questions

Is a JWT encrypted?

No — a standard signed JWT (JWS) is only signed, not encrypted. The header and payload are base64url-encoded, which anyone can decode and read. The signature only proves the token was not tampered with and was issued by someone holding the key. Never put secrets, passwords or sensitive personal data in a JWT payload. If you need confidentiality, use JWE (encrypted JWT) or keep the data server-side.

Where should I store a JWT?

For browser apps, an HttpOnly, Secure, SameSite cookie is generally safest because JavaScript cannot read it, which blocks token theft via XSS. localStorage and sessionStorage are readable by any script on the page, so a single XSS bug leaks the token — avoid them for sensitive apps. Native/mobile apps should use the platform secure store (Keychain / Keystore).

Can I revoke a JWT?

Not easily. A JWT is valid until it expires because verification is stateless — the server just checks the signature and claims, with no database lookup. To revoke early you must add state back: keep a short expiry (minutes), maintain a denylist of jti values, or track a token version per user. Refresh tokens make this practical: keep access tokens short-lived and revoke the refresh token.

HS256 or RS256 — which should I use?

Use HS256 (HMAC with a shared secret) when the same trusted party both issues and verifies tokens, e.g. a single monolithic backend. Use RS256 or ES256 (asymmetric) when tokens are issued by one service and verified by many, or by third parties — verifiers only need the public key and can never mint tokens. Most OAuth/OIDC providers use RS256 or ES256.

What is the nbf claim?

nbf ("not before") is a timestamp before which the token must be rejected, even if the signature is valid. It lets you issue a token that only becomes active in the future. Verifiers should check that the current time is at or after nbf, just as they check it is before exp.

Related tools