What Is a JWT and Why Decode It?
A JSON Web Token (JWT, pronounced "jot") is a compact, URL-safe token format defined by RFC 7519. It is the near-universal choice for stateless authentication in modern web APIs: after login, the server issues a signed token to the client; the client sends it back in every request; the server verifies the signature and trusts the claims inside without hitting a session database.
A JWT looks like eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c — three Base64URL-encoded segments joined by dots. You decode a JWT when you need to inspect what claims it carries: which user issued it, when it expires, what permissions or roles are attached, or which service audience it targets.
The Three JWT Segments
1. Header
The header declares two things: the signing algorithm (alg) and the token type (typ, always "JWT"). A typical HS256 header decodes to:
{
"alg": "HS256",
"typ": "JWT"
}Common alg values: HS256, HS384, HS512 (symmetric HMAC — same secret signs and verifies), RS256, RS384, RS512 (asymmetric RSA — private key signs, public key verifies), ES256, ES384 (asymmetric ECDSA), and PS256 (RSA-PSS). Never trust a JWT whose header says "alg": "none".
2. Payload
The payload is a JSON object of "claims" — key-value pairs about the subject. RFC 7519 defines seven standard claims and lets apps add any custom claims:
iss— issuer (who created the token, e.g."https://auth.example.com")sub— subject (the user or entity, e.g. a user id)aud— audience (which service should accept this token)exp— expiration (Unix timestamp, seconds since 1970)nbf— not before (token invalid until this Unix timestamp)iat— issued at (Unix timestamp when created)jti— JWT ID (unique identifier, used for revocation)
Custom claims are added freely: role, tenant_id, scope, email_verified. Namespace custom claims with a URL prefix to avoid collisions ("https://myapp.com/role").
3. Signature
The signature is a cryptographic hash of the encoded header + "." + encoded payload, computed using the algorithm from the header. For HS256:HMACSHA256(base64url(header) + "." + base64url(payload), secret). For RS256 the signature is an RSA signature over the same input using the issuer's private key.
The signature proves two things: (1) the token was created by someone holding the signing key, and (2) neither header nor payload has been modified since. Anyone can decode the payload; only the key holder can produce a valid signature.
Decoding vs Verifying — the Critical Difference
Decoding a JWT just Base64URL-decodes the header and payload. It requires no secret and returns the claims. This is safe for inspection but produces unverified data.
Verifyinga JWT recomputes the signature using the same algorithm and secret (or public key) and compares it to the signature segment. Only a verified JWT should be trusted for authentication decisions like "is this user logged in as admin?".
Never mix these up in production code. If your login endpoint accepts a JWT and only decodes it without verifying, an attacker can craft an arbitrary payload — including elevated permissions — and your server will trust it.
Common JWT Debugging Workflows
- "My API returns 401" — decode the JWT, check
exp. If it's in the past your token has expired; the client needs to refresh. - "My role/permission is missing" — decode the payload and check the custom claim your app relies on. If it's absent, the auth server didn't include it — check your identity provider's claim mapping.
- "Signature verification fails" — check the
algin the header matches what your verifier expects. HS256 uses a shared secret; RS256 uses a public key from a JWKS endpoint. - "Token works locally but not in production" — compare the
issandaudclaims. Auth servers issue different tokens per environment.
JWT Security Best Practices
- Always verify the signature — never trust a decoded-only JWT.
- Reject
alg: noneand enforce an allow-list of expected algorithms. - Set short expiry times (5–15 minutes) and use refresh tokens for longer sessions.
- Store JWTs in HTTP-only, Secure, SameSite=Strict cookies to prevent XSS theft.
- Never put passwords, credit card numbers, or PII in the payload — it is not encrypted.
- Rotate signing keys regularly and use a JWKS endpoint so verifiers pick up new keys automatically.
Key Facts
- Format:
- header.payload.signature (three Base64URL segments)
- Encoding:
- Base64URL (RFC 4648 §5) — like Base64 but + becomes -, / becomes _, no padding
- Encrypted?
- No — signed JWTs (JWS) are only encoded. Use JWE for encryption.
- Standard:
- RFC 7519 (JWT), RFC 7515 (JWS), RFC 7516 (JWE)
- Privacy:
- All processing in-browser — data never leaves your device
Related JWT Tools
- Decode JWT Token — dedicated guide with worked payload examples
- JWT Decoder in JavaScript — atob() + TextDecoder + jose library patterns
- JWT Decoder in Python — PyJWT decode & verify patterns
- Verify JWT Signature — HS256/RS256 signature verification walkthrough
- Base64 Encode — encode arbitrary strings to Base64
- Hash Generator — compute SHA-256, HMAC, MD5 hashes locally