Master JWT decoding for debugging authentication issues, inspecting token claims, and understanding web security. Learn the structure, security considerations, and best practices.
You're debugging a 401 Unauthorized error. You have a JWT token from the Authorization header, but what's inside it? Is the token expired? Does it have the right claims? Is the algorithm correct? To answer these questions, you need to decode the JWT—and understand what you're looking at.
Need to inspect a JWT token immediately? Use our free JWT decoder to view headers, payloads, and signatures instantly—100% client-side, your tokens never leave your browser.
Decode JWT Token →A JWT (JSON Web Token) is a compact, URL-safe way to represent claims to be transferred between two parties. It's the modern standard for authentication and authorization in web applications, APIs, and microservices.
The header specifies the signing algorithm (HMAC SHA256, RSA, etc.) and token type (JWT).
{
"alg": "HS256", // Algorithm: HMAC SHA-256
"typ": "JWT" // Token type
}Always validate the algorithm matches your application's expectations. Attackers can change "alg" from "RS256" (asymmetric) to "HS256" (symmetric) or even "none" to bypass signature verification.
Defense: Explicitly specify allowed algorithms in your JWT library configuration.
The payload contains claims—statements about the user and additional metadata. There are three types of claims:
| Claim Type | Examples | Purpose |
|---|---|---|
| Registered | iss, sub, exp, iat, aud | Standard claims defined by JWT specification |
| Public | name, email, admin | Custom claims defined by your application |
| Private | userId, companyId | Custom claims agreed between parties |
{
// Registered claims
"iss": "https://api.myapp.com", // Issuer
"sub": "user-12345", // Subject (user ID)
"aud": "https://myapp.com", // Audience
"exp": 1732752000, // Expiration time (Unix)
"iat": 1732748400, // Issued at (Unix)
"nbf": 1732748400, // Not before
// Public/custom claims
"name": "John Doe",
"email": "john@example.com",
"roles": ["admin", "user"],
"permissions": ["read:users", "write:posts"]
}The signature ensures the token hasn't been tampered with. It's created by:
encodedHeader.encodedPayloadDecoding reads the JWT's contents without checking validity. Verificationvalidates the signature to confirm authenticity and integrity.
Never trust decoded JWT data without verification. Anyone can create a JWT with any claims. Always verify signatures server-side before trusting token contents.
Privacy Note: All decoding happens in your browser using JavaScript. Tokens are never transmitted to any server.
# Decode JWT header (first part) echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d | jq # Decode JWT payload (second part) echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0" | base64 -d | jq # Decode both parts in one line JWT="eyJhbGci...full.token.here" echo $JWT | cut -d'.' -f1 | base64 -d | jq # Header echo $JWT | cut -d'.' -f2 | base64 -d | jq # Payload
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.xyz";
// Split JWT into parts
const [headerB64, payloadB64, signature] = jwt.split('.');
// Decode header
const header = JSON.parse(atob(headerB64));
console.log("Header:", header);
// Decode payload
const payload = JSON.parse(atob(payloadB64));
console.log("Payload:", payload);
// Check expiration
if (payload.exp && Date.now() >= payload.exp * 1000) {
console.log("Token expired!");
}import base64
import json
jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.xyz"
# Split and decode
header, payload, signature = jwt.split('.')
# Decode (add padding if needed)
def decode_jwt_part(part):
padding = 4 - len(part) % 4
part += '=' * padding
return json.loads(base64.urlsafe_b64decode(part))
header_data = decode_jwt_part(header)
payload_data = decode_jwt_part(payload)
print("Header:", header_data)
print("Payload:", payload_data)Symptom: API returns 401 with message "Token expired" or "Unauthorized"
Debug: Decode the JWT and check the exp claim
Convert to human-readable: new Date(1732748400 * 1000)
Solution: Refresh the token using your refresh token endpoint before it expires
Symptom: User can't access resources they should have permission for
Debug: Decode token and verify claims match database/expected values
Solution: Token was issued before role/permission change. Re-authenticate to get fresh token with updated claims
Symptom: Server rejects token with "Invalid signature" or "Algorithm not allowed"
Debug: Check token header's alg claim
Solution: Auth provider and API server must use the same algorithm. Update configuration or regenerate tokens
Symptom: Token rejected with "Invalid issuer" or "Invalid audience"
Debug: Verify iss and aud claims
Solution: Using dev token in production. Get token from correct environment
| Operation | Use Case | Trust Level |
|---|---|---|
| Decode Only |
| ❌ Never trust |
| Decode + Verify |
| ✅ Safe to trust |
| Claim | Full Name | Description | Example |
|---|---|---|---|
| iss | Issuer | Who created and signed the token | https://auth.myapp.com |
| sub | Subject | User identifier (user ID) | user-12345 |
| aud | Audience | Intended recipient of the token | https://api.myapp.com |
| exp | Expiration Time | When token expires (Unix timestamp) | 1732752000 |
| nbf | Not Before | Token not valid before this time | 1732748400 |
| iat | Issued At | When token was created | 1732748400 |
| jti | JWT ID | Unique token identifier (for revocation) | abc123xyz |
Many online JWT decoders transmit your tokens to backend servers for processing. This exposes sensitive authentication tokens to third parties.
Risks: Token theft, unauthorized access, data breaches, GDPR/compliance violations
Our JWT decoder runs entirely in your browser using JavaScript. Zero network requests. No data transmission. Your tokens never leave your device.
Yes. Decoding (reading the contents) doesn't require the secret key—only verification does. JWTs are Base64-encoded, not encrypted. Anyone can decode and read the header and payload. The signature validation is what requires the secret key.
No, standard JWTs (JWS - JSON Web Signature) are signed, not encrypted. The contents are Base64-encoded and visible to anyone. There's a separate standard called JWE (JSON Web Encryption) for encrypted JWTs, but it's rarely used.
Never put sensitive data in standard JWTs. Treat them as publicly readable.
Decode the token and check the exp claim. It's a Unix timestamp (seconds since 1970).
Access tokens (usually JWTs) grant access to APIs. Short-lived (15-30 min). Refresh tokens are long-lived (days/weeks) and used to obtain new access tokens without re-authenticating. Refresh tokens are often opaque (not JWTs) and stored server-side for revocation.
exp, iss, aud, and alg claims server-sideInspect headers, payloads, and signatures instantly with our privacy-first JWT decoder. No data transmission. No tracking. Just pure client-side decoding.
Decode JWT Token Now →