DevKits
Concept

How JWT (JSON Web Token) Works — Structure, Signing, Verification Explained

A JWT is a compact, URL-safe token for securely passing claims between parties. It consists of a Base64URL-encoded header, payload, and cryptographic signature. This guide explains what each part contains, how signing works, and the difference between HS256, RS256, and ES256.

Last updated:

Core Concepts

Three-part structure (header.payload.signature)
Every JWT is three Base64URL-encoded segments separated by dots. The header declares the algorithm (alg) and key ID (kid). The payload carries claims — standardized ones like exp (expiry), iat (issued-at), iss (issuer), aud (audience), and custom ones like user_id or role. The signature prevents tampering: changing a single byte in the payload invalidates it.
Symmetric vs asymmetric signing
HS256 (HMAC + SHA-256) uses a shared secret — both the signer and the verifier know the same string. RS256 (RSA) and ES256 (ECDSA) use a private key to sign and a public key to verify. Asymmetric keys let you safely publish the public key (e.g. at /.well-known/jwks.json) without exposing the signing secret. Pro tip: never use HS256 across service boundaries; if anyone on either side leaks the secret, they can forge tokens for every user.
Claims — exp, iat, nbf, aud, iss, sub
exp (expires-at) is the most critical: a Unix timestamp after which the token should be rejected. iat (issued-at) helps detect token replay. nbf (not-before) defers validity to a future time. aud (audience) restricts the token to a specific API. iss (issuer) identifies who minted it. sub (subject) is the user or resource the token represents. Claim validation is separate from signature validation — a perfectly-signed token can still be invalid if the claims don't check out.
The alg=none attack
Some older JWT libraries accepted tokens whose header claimed alg: 'none' (unsigned). An attacker can strip the signature, change the payload, and the server would trust it. Modern libraries reject alg: none by default, but explicit algorithm allowlisting (algorithms: ['RS256']) is the definitive defense — it prevents algorithm-confusion attacks where an RS256 public key gets reused as an HMAC secret.
JWKS (JSON Web Key Set)
A public endpoint (/.well-known/jwks.json) that lists an issuer's public keys, each with a kid. Verifiers fetch this set, pick the key whose kid matches the token header, and verify the signature. Key rotation works by adding a new key alongside the old one in the set — old tokens validate against the old key until they expire.

Frequently Asked Questions

How do I verify a JWT without a library?

The short answer is: don't. Use a battle-tested library (jsonwebtoken in Node, PyJWT, golang-jwt, jose). If you absolutely must: take the first two segments, reconstruct headerB64 + '.' + payloadB64, run the signing algorithm against your key, base64url-encode the raw signature, and compare. Any timing (string ==) instead of crypto.timingSafeEqual opens a side channel.

Should I use HS256 or RS256/ES256 for my API?

RS256 (RSA) or ES256 (ECDSA) for anything with more than one service. HS256 forces you to share a secret between signer and all verifiers — a breach on one leaks the key for everyone. RS256/ES256 let you publish a public key and keep the private key in one place. ES256 has smaller keys and faster signing than RS256 at equivalent security.

Try these related tools

JWT Verifier

Paste a JWT and its signing key (HMAC secret, RSA/EC public key in PEM or JWK) to verify the signature and inspect claims. Supports HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512. Also flags exp / nbf / iat time-based claims. 100% local — nothing is sent to any server.

JWT Generator

Create signed JSON Web Tokens with HS256/384/512 (HMAC), RS256/384/512 (RSA), PS256/384/512 (RSA-PSS), or ES256/384/512 (ECDSA). Paste a raw secret, a PEM private key, or a JWK — signing runs entirely in your browser via the Web Crypto API.

JWT Decoder

Decode JSON Web Tokens (JWT) to inspect the header, payload, and signature. Runs entirely in your browser — tokens are never sent to any server.

JWKS Generator

Assemble multiple RSA/EC public keys into a standard JSON Web Key Set (JWKS, RFC 7517). Paste PEM (public or private — private components are stripped) or JWK, then auto-generate kid via SHA-256 thumbprint (RFC 7638). Ready to host at /.well-known/jwks.json — free, no signup, 100% local.

RSA Key Generator

Generate RSA key pairs (2048, 3072, or 4096 bits) online for OAEP encryption, PSS signing, or RS256 JWTs. Exports PKCS#8 private key + SPKI public key as PEM, JWK, or DER (hex/base64). 100% local — the Web Crypto API runs in your browser, private keys never leave the tab.

ECDSA Key Generator

Generate elliptic-curve key pairs (P-256, P-384, P-521) for ECDSA signatures or ECDH key agreement. Exports PKCS#8 / SPKI PEM, JWK, and DER (hex/base64). Perfect for ES256/ES384/ES512 JWTs. 100% local via the Web Crypto API — the private key never leaves your browser.