JSON Web Tokens (JWTs) are everywhere in modern web authentication. If you've ever logged into a single-page application, called a protected API, or used OAuth, you've almost certainly used JWTs — even if you didn't know it. But what exactly is a JWT, and how do you decode, verify, and debug one?
This guide explains JWTs from the ground up, with practical examples and common debugging scenarios. When you need to inspect a token quickly, our JWT Decoder lets you paste and parse tokens instantly — everything runs in your browser with no data leaving your device.
What Is a JWT?
A JWT is a compact, URL-safe token format defined in RFC 7519. It consists of three parts separated by dots:
xxxxx.yyyyy.zzzzz
↑ ↑ ↑
Header Payload SignatureEach part is Base64url-encoded. Let's break them down.
Header
The header typically contains two fields:
{
"alg": "HS256",
"typ": "JWT"
}alg— the signing algorithm (HS256, RS256, ES256, etc.)typ— the token type (always "JWT")
Payload (Claims)
The payload contains claims — statements about the user and additional metadata:
{
"sub": "1234567890",
"name": "Alice",
"email": "alice@example.com",
"iat": 1711234567,
"exp": 1711238167,
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"roles": ["admin", "editor"]
}Standard (registered) claims include:
sub— subject (usually the user ID)iat— issued at (Unix timestamp)exp— expiration time (Unix timestamp)nbf— not before (Unix timestamp)iss— issuer (who created the token)aud— audience (who the token is for)jti— JWT ID (unique identifier for the token)
You can also add custom claims like roles, permissions, or any application-specific data.
Signature
The signature ensures the token hasn't been tampered with. For HMAC-based algorithms:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)For RSA/ECDSA algorithms, the signing uses a private key, and verification uses the corresponding public key. This is the standard approach for microservices, where services can verify tokens without knowing the signing secret.
How JWT Authentication Works
Here's the typical flow:
- Login: The user sends credentials (username + password) to the auth server.
- Token issuance: If credentials are valid, the server creates a JWT, signs it, and returns it to the client.
- Storage: The client stores the JWT (usually in memory, localStorage, or an HTTP-only cookie).
- API requests: The client sends the JWT in the
Authorizationheader:Bearer eyJhbGci... - Verification: The API server verifies the signature, checks expiration, and extracts the user info from the payload.
Decoding a JWT: Step by Step
Let's decode a real JWT. Here's a sample token (don't worry, it's not a real credential):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzExMjM0NTY3fQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cManual Decoding
// Split by dots
const [headerB64, payloadB64, signature] = token.split(".");
// Decode Base64url
const header = JSON.parse(atob(headerB64));
// { "alg": "HS256", "typ": "JWT" }
const payload = JSON.parse(atob(payloadB64));
// { "sub": "1234567890", "name": "Alice", "iat": 1711234567 }Note: atob() works for most JWTs, but for tokens with URL-safe Base64 characters, you need to replace - with + and _ with / first. Or just use our JWT Decoder which handles all edge cases.
Common JWT Debugging Scenarios
1. Token Expired (401 Unauthorized)
Decode the token and check the exp claim. Convert the Unix timestamp to a human-readable date using an epoch converter. If the token is expired, you need to refresh it.
2. Invalid Signature
This means the token was modified after signing, or the verification key is wrong. Check that your server is using the same secret/key that was used to sign the token.
3. Token Not Yet Valid
If the nbf (not before) claim is set to a future time, the token won't be accepted yet. Check server clock synchronization.
4. Wrong Audience
The aud claim must match what the verifying server expects. Mismatched audiences are a common issue in multi-service architectures.
JWT Security Best Practices
- Never store sensitive data in the payload. Remember, the payload is Base64-encoded, not encrypted. Anyone can decode it.
- Always verify the signature server-side. Never trust a JWT without verifying it.
- Use short expiration times. Access tokens should expire quickly (5–15 minutes). Use refresh tokens for longer sessions.
- Prefer RS256 over HS256 for microservices. RSA lets you share the public key for verification without exposing the signing key.
- Check the
algheader. The infamous "alg: none" attack tricks servers into accepting unsigned tokens. Always validate the algorithm. - Use HTTP-only cookies for browser storage when possible. This prevents XSS attacks from stealing the token.
JWTs vs Sessions
When should you use JWTs instead of traditional server-side sessions?
- Use JWTs when: You have a distributed system (multiple servers, microservices), need stateless authentication, or build a public API.
- Use sessions when: You have a monolithic app, need immediate revocation (logout), or want smaller request sizes.
Try It: Decode a JWT Instantly
Our JWT Decoder parses any JWT and displays the header, payload, and signature in a clean, readable format. It automatically converts timestamps to human-readable dates and highlights expired tokens.
Everything runs in your browser — no data is sent to any server. This makes it safe to decode production tokens containing real user data.
Working with Base64? Try the Base64 Encoder/Decoder. Need to format the JSON payload? Use the JSON Formatter.
Summary
JWTs are powerful but require understanding to use safely. Remember: the payload is readable by anyone, the signature provides integrity (not confidentiality), and expired tokens are the #1 debugging issue. Always verify server-side, keep tokens short-lived, and never store secrets in the payload.
When you need to quickly inspect a token, DevToolbox's JWT Decoder gives you instant, private results — no sign-up needed, no data leaving your machine.