What is JWT Decode?
JWT decode is the process of extracting and reading the information stored inside a JSON Web Token. When you decode a JWT, you convert the Base64URL-encoded header and payload back into readable JSON format. This allows you to inspect the token's claims, expiration time, and other metadata without needing the secret key.
Unlike JWT verification (which validates the signature), decoding simply reveals the token's contents. Anyone can decode a JWT—the information isn't encrypted, just encoded. This is an important security consideration: never store sensitive data like passwords in JWT payloads.
Try our free JWT Decoder Tool to instantly decode any JWT token in your browser.
Understanding JWT Structure
JWT Decoder & Validator
Decode and inspect JWT tokens instantly. View header, payload, and verify signatures with security validation.
Open the full JWT Decoder & Validator tool →Before diving into how to decode JWTs, let's understand their structure. A JWT consists of three parts separated by periods (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
This breaks down into:
| Part | Purpose | Example Decoded |
|---|---|---|
| Header | Algorithm & token type | {"alg":"HS256","typ":"JWT"} |
| Payload | Claims (user data) | {"sub":"1234567890","name":"John Doe","iat":1516239022} |
| Signature | Verification hash | Cannot be decoded to readable text |
The Header
The header contains metadata about the token:
{
"alg": "HS256",
"typ": "JWT"
}
- alg: The signing algorithm (HS256, RS256, ES256, etc.)
- typ: Token type (always "JWT")
The Payload (Claims)
The payload contains the actual data—called "claims." There are three types:
Registered Claims (standardized):
iss(issuer): Who created the tokensub(subject): Who the token is about (usually user ID)aud(audience): Who the token is intended forexp(expiration): When the token expires (Unix timestamp)iat(issued at): When the token was creatednbf(not before): Token not valid before this timejti(JWT ID): Unique identifier for the token
Public Claims: Custom claims registered with IANA (like email, name)
Private Claims: Custom claims agreed upon between parties
The Signature
The signature is created by:
- Encoding the header and payload
- Combining them with the secret key
- Applying the specified algorithm
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
The signature cannot be decoded into readable text—it's a cryptographic hash used only for verification.
How to Decode a JWT: Step-by-Step
Step 1: Split the Token
Separate the JWT into its three parts using the period (.) as a delimiter:
const parts = token.split('.');
const header = parts[0];
const payload = parts[1];
const signature = parts[2];
Step 2: Decode Base64URL
Each part (except the signature) is Base64URL encoded. Decode them to get JSON:
// Base64URL to Base64
function base64UrlToBase64(str) {
return str.replace(/-/g, '+').replace(/_/g, '/');
}
// Decode
const decodedHeader = JSON.parse(atob(base64UrlToBase64(header)));
const decodedPayload = JSON.parse(atob(base64UrlToBase64(payload)));
Step 3: Parse the JSON
After decoding, you'll have JavaScript objects you can work with:
console.log(decodedHeader);
// { alg: "HS256", typ: "JWT" }
console.log(decodedPayload);
// { sub: "1234567890", name: "John Doe", iat: 1516239022 }
JWT Decode Code Examples
JavaScript (Node.js)
Using the jsonwebtoken library:
const jwt = require('jsonwebtoken');
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Decode without verification
const decoded = jwt.decode(token);
console.log(decoded);
// { sub: '1234567890', name: 'John Doe', iat: 1516239022 }
// Decode with complete output (header + payload)
const complete = jwt.decode(token, { complete: true });
console.log(complete.header); // { alg: 'HS256', typ: 'JWT' }
console.log(complete.payload); // { sub: '1234567890', ... }
Vanilla JavaScript (Browser):
function decodeJWT(token) {
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid JWT format');
}
const base64Url = parts[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
return JSON.parse(jsonPayload);
}
const payload = decodeJWT(token);
console.log(payload.name); // "John Doe"
Python
Using PyJWT:
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# Decode without verification
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded)
# {'sub': '1234567890', 'name': 'John Doe', 'iat': 1516239022}
# Get header
header = jwt.get_unverified_header(token)
print(header)
# {'alg': 'HS256', 'typ': 'JWT'}
Without external libraries:
import base64
import json
def decode_jwt(token):
parts = token.split('.')
if len(parts) != 3:
raise ValueError("Invalid JWT format")
# Add padding if needed
payload = parts[1]
payload += '=' * (4 - len(payload) % 4)
decoded_bytes = base64.urlsafe_b64decode(payload)
return json.loads(decoded_bytes)
payload = decode_jwt(token)
print(payload['name']) # "John Doe"
Java
Using java-jwt (Auth0):
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
DecodedJWT decoded = JWT.decode(token);
// Access claims
String subject = decoded.getSubject();
String name = decoded.getClaim("name").asString();
Date expiresAt = decoded.getExpiresAt();
System.out.println("Subject: " + subject);
System.out.println("Name: " + name);
Using JJWT:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Claims;
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
// Split and decode payload only
String[] parts = token.split("\\.");
String payload = new String(Base64.getUrlDecoder().decode(parts[1]));
System.out.println(payload);
Go
Using golang-jwt:
package main
import (
"fmt"
"github.com/golang-jwt/jwt/v5"
)
func main() {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
// Parse without validation
token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{})
if err != nil {
panic(err)
}
if claims, ok := token.Claims.(jwt.MapClaims); ok {
fmt.Println("Subject:", claims["sub"])
fmt.Println("Name:", claims["name"])
}
}
PHP
Using firebase/php-jwt:
<?php
use Firebase\JWT\JWT;
$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
// Decode without verification
$parts = explode('.', $token);
$payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/')), true);
print_r($payload);
// Array ( [sub] => 1234567890 [name] => John Doe [iat] => 1516239022 )
Using Online JWT Decoders
For quick debugging, online tools are invaluable. Our JWT Decoder processes tokens entirely in your browser—your JWT never leaves your device.
When to use online decoders:
- Debugging authentication issues
- Inspecting token structure
- Learning JWT format
- Quick payload inspection
Security warning: Never paste production tokens containing real user data into untrusted online tools. Use tools that process client-side only, or create test tokens for debugging.
Real-World JWT Decode Use Cases
Understanding when and why to decode JWTs helps you use them effectively in your applications.
1. Displaying User Information in UI
After login, decode the JWT to show personalized content without making additional API calls:
// Client-side: Display user info from JWT
const token = localStorage.getItem('accessToken');
const payload = jwt.decode(token);
// Update UI with user data
document.getElementById('userName').textContent = payload.name;
document.getElementById('userEmail').textContent = payload.email;
document.getElementById('userRole').textContent = payload.role;
Important: This is safe for UI display, but never use decoded claims for authorization decisions on the client. Always validate server-side.
2. Checking Token Expiration Before API Calls
Prevent unnecessary API calls by checking expiration locally:
function isTokenValid(token) {
try {
const payload = jwt.decode(token);
if (!payload || !payload.exp) return false;
// Add 10-second buffer for clock skew
const expiresAt = payload.exp * 1000;
return Date.now() < (expiresAt - 10000);
} catch {
return false;
}
}
// Use before making API requests
async function fetchData() {
const token = getStoredToken();
if (!isTokenValid(token)) {
await refreshToken(); // Get new token first
}
return fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` }
});
}
3. Debugging Authentication Flows
When authentication fails, decode the JWT to identify issues:
function debugToken(token) {
const decoded = jwt.decode(token, { complete: true });
console.log('Algorithm:', decoded.header.alg);
console.log('Token Type:', decoded.header.typ);
console.log('Issuer:', decoded.payload.iss);
console.log('Audience:', decoded.payload.aud);
console.log('Subject:', decoded.payload.sub);
console.log('Issued At:', new Date(decoded.payload.iat * 1000));
console.log('Expires:', new Date(decoded.payload.exp * 1000));
console.log('Is Expired:', Date.now() > decoded.payload.exp * 1000);
}
4. Implementing Role-Based UI (Client-Side)
Show or hide UI elements based on user roles (while enforcing on the server):
const payload = jwt.decode(token);
const userRoles = payload.roles || [];
// Show admin panel link if user has admin role
if (userRoles.includes('admin')) {
document.getElementById('adminLink').style.display = 'block';
}
// Show premium features for subscribers
if (payload.subscription === 'premium') {
enablePremiumFeatures();
}
5. Multi-Tenant Application Routing
In SaaS applications, decode JWTs to route users to their tenant:
const payload = jwt.decode(token);
const tenantId = payload.tenant_id;
// Route to tenant-specific subdomain or path
if (tenantId) {
window.location.href = `https://${tenantId}.app.example.com/dashboard`;
}
JWT Libraries Comparison
Choosing the right JWT library depends on your language and requirements:
JavaScript/Node.js Libraries
| Library | Decode | Verify | Sign | Notes |
|---|---|---|---|---|
jsonwebtoken | ✅ | ✅ | ✅ | Most popular, full-featured |
jose | ✅ | ✅ | ✅ | Modern, supports JWE encryption |
jwt-decode | ✅ | ❌ | ❌ | Lightweight, decode-only |
// jsonwebtoken (recommended for Node.js)
const jwt = require('jsonwebtoken');
const decoded = jwt.decode(token);
// jose (recommended for Edge/browser)
import * as jose from 'jose';
const { payload } = jose.decodeJwt(token);
// jwt-decode (minimal, client-side)
import jwt_decode from 'jwt-decode';
const decoded = jwt_decode(token);
Python Libraries
| Library | Decode | Verify | Sign | Notes |
|---|---|---|---|---|
PyJWT | ✅ | ✅ | ✅ | Most popular, well-maintained |
python-jose | ✅ | ✅ | ✅ | JOSE standard support |
authlib | ✅ | ✅ | ✅ | Full OAuth/OIDC framework |
Java Libraries
| Library | Decode | Verify | Sign | Notes |
|---|---|---|---|---|
java-jwt (Auth0) | ✅ | ✅ | ✅ | Simple API, well-documented |
jjwt | ✅ | ✅ | ✅ | Fluent API, extensive features |
nimbus-jose-jwt | ✅ | ✅ | ✅ | Full JOSE support, enterprise-ready |
Choosing a Library
- For decode-only (client-side): Use lightweight libraries like
jwt-decode - For full JWT handling: Use established libraries like
jsonwebtokenorPyJWT - For encryption (JWE): Use JOSE-compliant libraries like
joseorpython-jose - For OAuth/OIDC flows: Consider framework libraries like
authlib
JWT Decode vs JWT Verify
Understanding the difference is critical for security:
| Action | What It Does | Needs Secret Key? | Use Case |
|---|---|---|---|
| Decode | Reads token contents | No | Debugging, displaying user info |
| Verify | Validates signature + claims | Yes | Authentication, authorization |
Critical security rule: Always verify JWTs before trusting their contents for authorization decisions. Decoding alone does NOT prove the token is legitimate.
// WRONG - Security vulnerability!
const decoded = jwt.decode(token);
if (decoded.role === 'admin') {
grantAdminAccess(); // Anyone can create a token with role: admin
}
// CORRECT - Verify first
try {
const verified = jwt.verify(token, secretKey);
if (verified.role === 'admin') {
grantAdminAccess(); // Signature proves token is authentic
}
} catch (err) {
denyAccess();
}
Learn more about verification in our guide: How to Verify JWT Signatures.
Common JWT Decoding Errors
"Invalid token format"
Cause: Token doesn't have exactly 3 parts separated by periods.
Solution: Ensure you're copying the complete token without extra whitespace or line breaks.
// Check format before decoding
function isValidJWTFormat(token) {
const parts = token.split('.');
return parts.length === 3 && parts.every(part => part.length > 0);
}
"Malformed UTF-8 data"
Cause: Incorrect Base64URL decoding or encoding issues.
Solution: Use proper Base64URL decoding (replace - with + and _ with /):
function base64UrlDecode(str) {
// Replace URL-safe characters
str = str.replace(/-/g, '+').replace(/_/g, '/');
// Add padding
while (str.length % 4) str += '=';
return atob(str);
}
"Unexpected token in JSON"
Cause: Decoded string isn't valid JSON (possibly decoding the signature).
Solution: Only decode header (index 0) and payload (index 1), not signature (index 2).
Token shows as expired
Cause: The exp claim timestamp has passed.
Solution: This isn't a decoding error—the token is legitimately expired. Request a new token from your auth server.
function isTokenExpired(token) {
const decoded = jwt.decode(token);
if (!decoded.exp) return false;
return Date.now() >= decoded.exp * 1000;
}
Security Best Practices
What NOT to Store in JWTs
JWTs are encoded, not encrypted. Never include:
- Passwords or password hashes
- Credit card numbers
- Social Security numbers
- API keys or secrets
- Sensitive personal information
Safe JWT Handling
- Always use HTTPS - Prevents token interception
- Set short expiration times - Limit damage if compromised (15-60 minutes typical)
- Use httpOnly cookies - Prevents XSS from stealing tokens
- Implement token refresh - Use refresh tokens for long sessions
- Validate on the server - Never trust client-side validation alone
Detecting Tampered Tokens
If someone modifies a JWT payload, the signature becomes invalid:
// Server-side verification catches tampering
try {
const verified = jwt.verify(token, process.env.JWT_SECRET);
// Token is authentic and unmodified
} catch (err) {
if (err.name === 'JsonWebTokenError') {
console.log('Token was tampered with or invalid');
}
}
JWT Decode FAQ
Is it safe to decode JWTs online?
Yes, if the tool processes tokens client-side (in your browser). Our JWT Decoder never sends your token to any server. However, avoid pasting production tokens with real user data into untrusted tools.
Can anyone decode my JWT?
Yes. JWTs are encoded (Base64URL), not encrypted. Anyone with the token can read the header and payload. This is why you should never store sensitive data in JWTs and always use HTTPS.
Why can't I decode the signature?
The signature is a cryptographic hash, not encoded data. It's designed to be verified (compared), not decoded. You need the secret key to verify it matches the expected value.
How do I decode a JWT in the browser console?
JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')))
What's the difference between Base64 and Base64URL?
Base64URL is URL-safe: it replaces + with - and / with _, and may omit padding (=). JWTs use Base64URL because tokens are often passed in URLs.
Learn more: Base64URL and Base64 Variants Explained
Related Tools and Resources
- JWT Decoder Tool - Decode JWTs instantly in your browser
- Base64 Encoder/Decoder - Encode and decode Base64 strings
- Hash Generator - Generate SHA256, MD5, and other hashes
- How to Verify JWT Signatures - Complete verification guide
- Is JWT Decoding Safe? - Security considerations
- What is a JWT Token? - JWT fundamentals explained
JWT Decode Troubleshooting Checklist
When JWT decoding isn't working as expected, run through this checklist:
- Verify token format: Ensure exactly 3 parts separated by periods
- Check for whitespace: Trim any leading/trailing spaces or newlines
- Confirm encoding: JWTs use Base64URL, not standard Base64
- Test with known-good token: Use a sample JWT to verify your code works
- Check character encoding: Ensure UTF-8 encoding throughout
- Verify JSON parsing: The decoded string must be valid JSON
- Handle padding: Add
=padding characters if missing (some libraries require this)
// Comprehensive JWT validation before decoding
function validateAndDecode(token) {
// Remove whitespace
token = token.trim();
// Check format
const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid JWT: must have 3 parts');
}
// Validate each part has content
if (parts.some(p => p.length === 0)) {
throw new Error('Invalid JWT: empty segment');
}
// Decode and parse
try {
return JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
} catch (e) {
throw new Error(`JWT decode failed: ${e.message}`);
}
}
Conclusion
JWT decode is a fundamental skill for any developer working with modern authentication. Whether you're debugging auth flows, inspecting token claims, or building integrations, understanding how to properly decode JWTs saves hours of troubleshooting.
Key takeaways:
- JWTs have three parts: header, payload, and signature
- Decoding reveals contents but doesn't verify authenticity
- Always verify signatures before trusting token claims
- Never store sensitive data in JWT payloads
- Use our JWT Decoder Tool for quick, secure decoding
Need to decode a JWT right now? Try our free JWT Decoder—it works entirely in your browser with no data sent to servers.