JWT (JSON Web Token) Complete Guide 2026 — Structure, Security, Production Patterns
Complete reference: structure (header.payload.signature), 10 algorithms benchmarked, 7 common attacks + mitigations, library recommendations across 7 languages, and the modern best-practice patterns: short access tokens + refresh rotation + denylist for revocation.
Updated April 2026 · RFC 7519 + OWASP 2026 + OAuth 2.0 BCP RFC 8252
JWT signing algorithms (10 options benchmarked)
| Algorithm | Type | Key | Sign speed | Verify speed | Best for |
|---|---|---|---|---|---|
| HS256 | HMAC | Shared secret | ~250k ops/sec | ~250k ops/sec | Single backend, simple deployments |
| HS384 | HMAC | Shared secret | ~200k | ~200k | Higher security HMAC |
| HS512 | HMAC | Shared secret | ~150k | ~150k | Maximum HMAC security |
| RS256 | RSA | Private/public key pair | ~600 ops/sec | ~30k | Multi-service auth, OIDC standard |
| RS384 | RSA | Private/public key pair | ~600 | ~30k | Higher security RSA |
| RS512 | RSA | Private/public key pair | ~550 | ~28k | Maximum RSA security |
| ES256 | ECDSA P-256 | EC key pair | ~10k | ~5k | Modern stacks, smaller signatures |
| ES384 | ECDSA P-384 | EC key pair | ~5k | ~2.5k | Higher security ECDSA |
| ES512 | ECDSA P-521 | EC key pair | ~3k | ~1.5k | Maximum ECDSA security |
| EdDSA (Ed25519) | Edwards curve | EC key pair | ~25k | ~9k | Modern best-practice 2026, fast + secure |
Speeds benchmarked on AMD EPYC 7763 (1 core), April 2026, jose v5.9. Real-world performance varies; ECDSA verify slower than sign.
JWT library recommendations by language
| Language | Library | Version | GH ⭐ | Recommendation |
|---|---|---|---|---|
| Node.js | jose | 5.9+ | 5.4k | ✅ Modern, edge-runtime compatible |
| Node.js | jsonwebtoken | 9.0+ | 17.5k | ⚠️ Legacy, callback API, vulnerable history |
| Python | PyJWT | 2.9+ | 5.2k | ✅ Recommended |
| Python | python-jose | 3.3+ | 1.5k | ⚠️ Maintenance unclear 2025+ |
| Python | authlib | 1.3+ | 4.8k | ✅ For OAuth/OIDC stacks |
| Go | golang-jwt/jwt | v5+ | 7.5k | ✅ Standard |
| Go | lestrrat-go/jwx | v3+ | 2k | ✅ Full JOSE suite |
| Java | auth0/java-jwt | 4.4+ | 6.4k | ✅ Recommended for backends |
| Java | jjwt | 0.12+ | 10.4k | ✅ Most popular |
| Rust | jsonwebtoken | 9+ | 1.7k | ✅ Performant, type-safe |
Frequently asked questions
What is a JWT and how is it structured?▼
A JWT (JSON Web Token, RFC 7519) is a compact, URL-safe token format with three Base64URL-encoded parts joined by dots: HEADER.PAYLOAD.SIGNATURE. (1) HEADER: JSON object specifying algorithm + token type, e.g. {"alg":"HS256","typ":"JWT"}. (2) PAYLOAD (claims): JSON with reserved claims (iss=issuer, sub=subject, aud=audience, exp=expiration, nbf=not-before, iat=issued-at, jti=unique ID) plus custom claims (e.g., role, email, tenant_id). (3) SIGNATURE: HMAC or RSA/ECDSA over base64url(header) + "." + base64url(payload) using the algorithm specified in header. EXAMPLE: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTcyNzU0MDgwMH0.5yVN4pNdN-XJ8mz... CRITICAL: payload is NOT encrypted, only signed — anyone can read claims. Use JWE (encrypted JWT, RFC 7516) if you need confidentiality. Token size: typical JWT 200-500 bytes; large session JWTs 1-2KB. Goes in `Authorization: Bearer <token>` header for API auth.
HMAC (HS256) vs RSA (RS256) vs ECDSA (ES256) — which to use?▼
Algorithm decision 2026: USE HS256 IF: single backend service signs + verifies. Same secret on both ends. Pros: 250x faster than RS256, simpler key mgmt. Cons: any service with the secret can FORGE tokens — bad for multi-service. USE RS256 IF: multiple services need to verify but only auth service signs. Pros: public key can be safely distributed (.well-known/jwks.json), industry standard for OIDC. Cons: 2048-bit keys, slow signing. USE ES256 IF: same use case as RS256 but want faster + smaller signatures. Pros: 16x faster signing than RSA, 64-byte signatures vs 256-byte. Cons: less library support than RSA, more complex curve params. USE EdDSA (Ed25519) IF: greenfield 2026 project, all libraries support it. Pros: best modern algorithm — faster than ECDSA, simpler implementation, deterministic signatures. Cons: no FIPS 140-2 cert (compliance issue for some govt/finance). DO NOT USE: HS512 if HS256 enough (only 2x slower for marginal security gain), RS-PSS variants (compatibility issues), `alg:none` (always reject — historic CVE source). Per OWASP 2026: prefer EdDSA > ES256 > RS256 > HS256 in greenfield.
What are the most common JWT security vulnerabilities?▼
Top 7 JWT vulnerabilities 2026: (1) ALG:NONE attack — token claims `"alg":"none"` and library accepts unsigned tokens. Mitigation: never accept "none", whitelist allowed algorithms only. (2) ALG CONFUSION (RS256 → HS256) — attacker changes header to HS256 and signs with public key as "secret". Mitigation: explicit algorithm allowlist per-token, use libraries with type-safe verify. (3) WEAK SECRETS for HMAC — short/predictable secrets break HS256 in seconds via brute force. Mitigation: minimum 256-bit random secret, use HKDF-derived keys. (4) KEY CONFUSION — attacker submits crafted token referencing public verification key as if it were HMAC secret. Mitigation: separate keys per algorithm. (5) NO EXPIRATION CHECK — token has `exp` claim but app forgets to check it. Mitigation: library that enforces exp by default. (6) JKU/JWKS HEADER INJECTION — attacker sets jku header pointing to attacker-controlled key URL. Mitigation: always allowlist jku domains. (7) LONG-LIVED TOKENS — 30-day JWT with no revocation = stolen token grants 30-day access. Mitigation: short-lived (15-min) access tokens + refresh tokens with rotation. CVE-2022-23529, CVE-2017-11424, CVE-2015-9235 are infamous past JWT library bugs.
How long should a JWT live? Short vs long expiration tradeoffs.▼
JWT expiration design 2026: ACCESS TOKENS — 5-15 minutes. Best practices per OAuth 2.0 BCP (RFC 8252) + Auth0/Okta defaults. REASONING: stolen tokens limit blast radius. If 15-min access token leaks via logs/XSS, attacker has 15 min max. REFRESH TOKENS — 30 days to 1 year, MUST be opaque (random ID, not JWT) and stored in revocation table. REASONING: refresh tokens are higher-value; need revocation capability that JWTs cannot provide. ID TOKENS (OIDC) — 1 hour standard, used by client to identify user (not for API auth). FORMULA: expiration_choice = balance(user_friction_acceptable, blast_radius_tolerable). USER FRICTION: 5-min token = re-auth every 5 min annoying for SPAs without refresh. 60-min token = better UX but worse if leaked. PATTERNS: SPA + Browser: 15-min access + 30-day refresh in HttpOnly cookie. Mobile App: 15-min access + 90-day refresh. Service-to-Service: 1-hour access + no refresh (re-auth via mTLS). Critical: ALWAYS check `exp` server-side. NEVER trust client clocks. Use `nbf` (not-before) to prevent backdated tokens. Use `iat` (issued-at) for token age tracking + revocation by issued-time threshold ("revoke all tokens issued before timestamp X").
Where should I store JWTs in the browser?▼
Browser storage decision 2026: HTTPONLY COOKIE (RECOMMENDED) — JS cannot read it (XSS-resistant), browser auto-sends with same-origin requests. Pros: XSS attacks cannot steal token. Cons: requires CSRF protection (use SameSite=Strict + double-submit CSRF token). LOCAL STORAGE — DO NOT USE for sensitive tokens. Pros: easy. Cons: ANY XSS = full token theft. ANY 3rd-party script (analytics, ads, deps) can read. SESSION STORAGE — same XSS issues as localStorage but cleared on tab close. MEMORY (JS variable / Auth0 SDK pattern) — store token in JS memory, refresh from refresh-token cookie on page load. Pros: stolen browser memory survives less well. Cons: lost on page refresh (need silent refresh). BROWSER MEMORY + REFRESH COOKIE = current best practice 2026 for SPAs. INDEXEDDB — overkill, no security advantage. AUTH0/CLERK/SUPABASE all default to memory + refresh-cookie pattern. CRITICAL ANTI-PATTERN: storing 30-day refresh token in localStorage = same risk as session, defeats purpose. Refresh tokens MUST be HttpOnly cookies or backend-encrypted storage.
How do I revoke a JWT?▼
JWT REVOCATION CHALLENGE: JWTs are stateless by design — server doesn't track them. Without modification you CANNOT revoke a JWT before its `exp`. Solutions 2026: (1) SHORT EXPIRATION + REFRESH PATTERN — access token 15 min, refresh token in revocation list. Most practical solution. To "log out", revoke the refresh token. Existing access tokens still work for ≤15 min then user must re-auth. (2) DENYLIST (BLACKLIST) — store revoked `jti` (JWT ID) claims in Redis with TTL = remaining token life. Every JWT verify also checks denylist. Trade-off: server now stateful per-token. Use only for SECURITY EVENTS (compromised account), not normal logout. (3) ALLOWLIST (WHITELIST) — store every issued token. JWT validation requires both signature AND allowlist hit. Heavy server cost but full control. (4) ROTATION KEY — change signing key every N hours; tokens signed with old key automatically rejected. Mass-revocation by rotating key. Useful for emergency. (5) CLAIMS CHECK — embed `version` claim, increment server-side per user; reject JWTs with old version. Cheap per-user invalidation. Most production systems use COMBO: short access tokens + refresh denylist + rotation key for emergencies.
JWT vs session cookies — which to use in 2026?▼
Decision matrix 2026: USE JWT IF: (1) microservices/distributed system needing decentralized auth verification. (2) mobile/native apps without cookie support. (3) cross-domain SSO. (4) machine-to-machine API auth. (5) tokens carry rich claims used by multiple services. USE SESSION COOKIES IF: (1) monolithic web app. (2) need easy revocation. (3) team unfamiliar with JWT pitfalls. (4) low-volume site (server-side session lookup is cheap). (5) need to update permissions instantly. PROS JWT: stateless (scales horizontally), works across domains, mobile-friendly, claims travel with token, no DB lookup per request. CONS JWT: cannot be revoked easily, tokens are larger than cookie session IDs, payload exposed (not encrypted), implementation complexity (signature attacks). PROS SESSION: trivial revocation (delete server-side row), small cookie (just session ID), permissions update instantly, simpler. CONS SESSION: requires server-side store (Redis/DB), harder to scale, harder cross-domain. HYBRID: many production systems use SESSION cookies for browser auth + JWT for service-to-service. Per OWASP Application Security Verification Standard 2026: both are acceptable; JWT requires stricter implementation review.
What JWT libraries should I use in 2026?▼
Library recommendations 2026 (vetted for active maintenance + security): NODE.JS — `jose` v5.9+ (panva/jose, edge-compatible, modern API). Avoid `jsonwebtoken` v9 unless legacy stack (callback API, historical CVEs). PYTHON — `PyJWT` 2.9+ (recommended) or `authlib` 1.3+ for full OIDC stacks. GO — `golang-jwt/jwt` v5 (most popular) or `lestrrat-go/jwx` v3 for full JOSE suite. JAVA — `jjwt` 0.12+ (most popular) or `auth0/java-jwt` for cleaner API. RUST — `jsonwebtoken` 9+ (Keats/jsonwebtoken). C# — `Microsoft.IdentityModel.Tokens` (official Microsoft, OIDC-aligned). PHP — `firebase/php-jwt` 6.10+. RUBY — `ruby-jwt` 2.9+. Cross-language KEY THINGS TO CHECK: (1) library defaults algorithm allowlist (rejects `alg:none`). (2) enforces `exp` by default. (3) supports recommended algorithms (ES256, EdDSA, RS256). (4) recent commits (within 6 months — abandonware = security risk). (5) public CVE history reviewed. AVOID DEPRECATED: `pyjwt` <2.0, `jsonwebtoken` <8.0, `jjwt` <0.11.5, `golang-jwt/jwt` v3 (rename happened, v3 unmaintained).