JSON Web Tokens (JWTs) are the backbone of modern API authentication. Every time you log into a web app, every time a mobile app calls an API, and every time a microservice communicates with another — there is likely a JWT involved. Understanding how to decode and inspect these tokens is essential for debugging authentication issues, verifying claims, and building secure applications.
This guide explains the complete structure of JWT tokens, shows you how to decode them, covers security implications you need to know, and introduces Risetop's free JWT decoder tool for instant token inspection.
What Is a JWT?
A JSON Web Token is a compact, URL-safe means of representing claims to be transferred between two parties. The claims are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure.
In simpler terms: a JWT is a string that contains information about a user or session, signed by the server so it cannot be tampered with. It looks like this:
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
The three parts — colored red, purple, and cyan above — are the header, payload, and signature, separated by dots.
JWT Structure Deep Dive
1. Header
The header identifies the token type and the signing algorithm. It is Base64URL-encoded JSON:
// Decoded header
{
"alg": "HS256",
"typ": "JWT"
}
Common algorithms include:
| Algorithm | Type | Description |
HS256 | HMAC + SHA-256 | Symmetric — same secret for signing and verifying |
HS384 | HMAC + SHA-384 | Symmetric — stronger HMAC |
HS512 | HMAC + SHA-512 | Symmetric — strongest HMAC |
RS256 | RSA + SHA-256 | Asymmetric — private key signs, public key verifies |
RS384 | RSA + SHA-384 | Asymmetric — stronger RSA |
ES256 | ECDSA + P-256 + SHA-256 | Asymmetric — smaller signatures than RSA |
PS256 | RSA-PSS + SHA-256 | Asymmetric — RSA with probabilistic signature |
EdDSA | Ed25519 | Asymmetric — modern, fast, compact |
2. Payload
The payload contains the claims — statements about an entity (typically the user) and additional metadata. It is also Base64URL-encoded JSON:
// Decoded payload
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022,
"exp": 1516242622
}
Registered Claims (Standard)
| Claim | Full Name | Description |
iss | Issuer | Who issued the token (e.g., your API URL) |
sub | Subject | The user ID or entity the token represents |
aud | Audience | Intended recipients of the token |
exp | Expiration | Unix timestamp when the token expires |
nbf | Not Before | Token is not valid before this time |
iat | Issued At | Unix timestamp when the token was created |
jti | JWT ID | Unique identifier for the token (prevents replay) |
Custom Claims
You can add any custom claims to the payload — user roles, permissions, profile data, or application-specific information:
{
"sub": "user_42",
"roles": ["admin", "editor"],
"permissions": ["read:users", "write:posts"],
"org_id": "org_123",
"tier": "premium",
"exp": 1744358400
}
3. Signature
The signature is created by combining the encoded header, encoded payload, and a secret (or private key) using the algorithm specified in the header:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
The signature ensures that the token has not been tampered with. If anyone modifies even a single character in the header or payload, the signature verification will fail.
Important: Base64URL encoding is NOT encryption. Anyone can decode the header and payload and read the contents. Never put sensitive data (passwords, credit card numbers, private keys) in a JWT payload. If you need to protect the payload from being read, use JWE (JSON Web Encryption) instead.
How to Decode a JWT
Online JWT Decoder (Easiest)
Risetop's JWT Decoder lets you paste any JWT and instantly see the decoded header, payload, and signature information. It shows timestamps in human-readable format, highlights expiration status, and warns about common security issues — all in your browser, with no data sent to any server.
JavaScript
// Decode without verification (for debugging only!)
function decodeJwt(token) {
const [header, payload] = token.split('.');
return {
header: JSON.parse(atob(header.replace(/-/g, '+').replace(/_/g, '/'))),
payload: JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')))
};
}
// With the jose library (recommended for production)
import * as jose from 'jose';
const { payload, protectedHeader } = await jose.jwtDecode(token);
Python
import jwt
import json
# Decode without verification (debugging only)
decoded = jwt.decode(token, options={"verify_signature": False})
print(json.dumps(decoded, indent=2))
# With verification (production)
decoded = jwt.decode(token, "your-secret-key", algorithms=["HS256"])
print(decoded['sub'])
Command Line
# Decode the payload using cut and base64
echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0" | \
base64 -d 2>/dev/null; echo
# Using jwt-cli (npm)
npx jwt-decode "your.jwt.token"
# Using jwt.io (open in browser)
# https://jwt.io/#debugger-io
JWT Authentication Flow
Understanding the typical JWT authentication flow helps you know when and why you would need to decode a token:
- Login: The client sends credentials (username/password) to the auth server.
- Token issuance: The server verifies credentials and returns a signed JWT.
- Storage: The client stores the JWT (typically in memory or httpOnly cookies — not localStorage for security-sensitive apps).
- API requests: The client sends the JWT in the
Authorization: Bearer <token> header with each request.
- Verification: The server verifies the signature and checks claims (expiration, issuer, audience).
- Response: If valid, the server processes the request and returns data.
Security Best Practices
- Always verify the signature — Decoding without verifying tells you what the token claims, but not whether those claims are legitimate.
- Set short expiration times — Access tokens should expire in 15-30 minutes. Use refresh tokens (stored securely) for longer sessions.
- Use strong algorithms — Prefer RS256 or ES256 over HS256 for distributed systems. HS256 requires the same secret on all services that verify tokens.
- Validate all claims — Check
exp, nbf, iss, aud, and sub. Don't just verify the signature.
- Store tokens securely — Use httpOnly, Secure, SameSite cookies when possible. Avoid localStorage for sensitive tokens (vulnerable to XSS).
- Use the
jti claim for token revocation — Store active JWT IDs in a database or cache to support logout and revocation.
- Never put secrets in the payload — The payload is Base64URL-encoded, not encrypted. Anyone can read it.
Common vulnerability: The "none" algorithm attack. Some JWT libraries accepted "alg": "none", which means no signature verification. Always enforce a whitelist of allowed algorithms on the server side. Never trust the algorithm from the token header.
Common JWT Problems and How to Debug Them
| Problem | Cause | Solution |
jwt expired | Token exp claim has passed | Refresh the token using a refresh token |
invalid signature | Wrong secret/key or token tampered | Verify you are using the correct signing key |
malformed jwt | Token is not properly formatted | Check for three dot-separated Base64URL segments |
invalid audience | aud claim doesn't match expected value | Ensure the token was issued for your service |
not enough segments | Token is truncated or malformed | Check for whitespace or missing parts |
A JWT decoder is your first debugging tool. By decoding the token, you can immediately see the expiration time, the algorithm used, the claims, and whether the structure is valid — all of which point you toward the root cause of the problem.
Frequently Asked Questions
Is it safe to decode a JWT token?
Yes, decoding the header and payload of a JWT is completely safe and is designed to be done by anyone. The header and payload are Base64URL-encoded (not encrypted), so anyone can read them. Only the signature is cryptographically protected. Decoding a JWT reveals its contents but does not verify its authenticity — that requires the secret key.
What is the difference between JWT encoding and encryption?
Encoding (standard JWT) uses Base64URL to represent data, which anyone can reverse. Encryption (JWE — JSON Web Encryption) uses cryptographic algorithms to make the payload unreadable without the decryption key. If your JWT contains sensitive data that the client should not see, use JWE instead of standard JWT.
How can I tell if a JWT has been tampered with?
You verify the JWT signature using the server's secret key (for HMAC) or public key (for RSA/ECDSA). If the signature verification fails, the token has been tampered with and should be rejected. Never trust a JWT's claims without verifying its signature first.
What is the difference between JWS, JWE, and JWK?
JWS (JSON Web Signature) is a signed JWT — the most common type. It ensures integrity but the payload is readable. JWE (JSON Web Encryption) encrypts the payload so only parties with the key can read it. JWK (JSON Web Key) is a standard format for representing cryptographic keys. Together they form the JOSE (JSON Object Signing and Encryption) family.
What happens when a JWT expires?
When a JWT's "exp" (expiration) claim passes the current time, the token should be rejected by the server. The client will receive a 401 Unauthorized response and needs to obtain a new token — typically by using a refresh token or re-authenticating. Most JWT libraries handle expiration checking automatically during verification.
Related Tools
Decode Any JWT Instantly
Paste a token, see the decoded header, payload, and signature analysis — all client-side, no data sent to servers.
Open JWT Decoder →