What Is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe way to represent claims between two parties. It's the most common authentication token format in modern web development — used by Auth0, Firebase, Supabase, AWS Cognito, and nearly every OAuth 2.0 provider.
A JWT looks like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
That's three Base64URL-encoded parts separated by dots:
- Header — algorithm and token type
- Payload — claims (user data, expiration, permissions)
- Signature — verification hash
JWT Structure Explained
Header
{
"alg": "HS256",
"typ": "JWT"
}
The header tells you which algorithm was used to sign the token. Common algorithms:
| Algorithm | Type | Key | Use Case |
|---|---|---|---|
| HS256 | HMAC | Shared secret | Simple apps, single server |
| HS384/HS512 | HMAC | Shared secret | Higher security symmetric |
| RS256 | RSA | Public/private pair | Distributed systems, OAuth |
| RS384/RS512 | RSA | Public/private pair | High security RSA |
| ES256 | ECDSA | Public/private pair | Modern apps, smaller keys |
| EdDSA | EdDSA | Public/private pair | Newest, fastest asymmetric |
Security tip: Never accept
"alg": "none"in production. This disables signature verification entirely — a classic JWT attack vector.
Payload (Claims)
{
"sub": "user_123",
"name": "Alice",
"email": "alice@example.com",
"role": "admin",
"iat": 1712016000,
"exp": 1712102400,
"iss": "https://auth.example.com"
}
Claims are the data inside the token. There are three types:
Registered Claims (Standard)
| Claim | Full Name | Purpose |
|---|---|---|
iss | Issuer | Who created the token |
sub | Subject | Who the token is about (usually user ID) |
aud | Audience | Who the token is intended for |
exp | Expiration | When the token expires (Unix timestamp) |
nbf | Not Before | Token not valid before this time |
iat | Issued At | When the token was created |
jti | JWT ID | Unique identifier for the token |
Public Claims
Custom claims registered in the IANA JWT Claims Registry: name, email, email_verified, picture, etc.
Private Claims
Your custom claims: role, permissions, tenant_id, plan — anything your application needs.
Why Decode JWTs?
Common debugging scenarios:
- "Why is my API returning 401?" → Token might be expired. Decode and check
exp. - "User has wrong permissions" → Decode and check
roleorscopeclaims. - "Token works on staging but not production" → Check
issandaudclaims. - "How long is this token valid?" → Compare
iatandexptimestamps. - "What algorithm does this provider use?" → Check the header
algfield.
What Our JWT Decoder Does
Our free JWT decoder gives you:
- Instant decoding — paste a JWT, see header + payload immediately
- Expiration check — shows if the token is expired, with time remaining or time since expiry
- Timestamp conversion —
iat,exp,nbfshown as human-readable dates - Color-coded sections — header (red), payload (purple), signature (cyan)
- Copy individual sections — one-click copy of header or payload JSON
- 100% client-side — your token never leaves your browser
⚠️ Never paste production tokens into online tools that send data to a server. Our decoder runs entirely in JavaScript — check the source yourself.
Decoding JWTs in Code
JavaScript (No Library)
function decodeJWT(token) {
const parts = token.split('.');
return {
header: JSON.parse(atob(parts[0])),
payload: JSON.parse(atob(parts[1]))
};
}
// Check expiration
function isExpired(token) {
const { payload } = decodeJWT(token);
return payload.exp < Math.floor(Date.now() / 1000);
}
Node.js (jsonwebtoken)
const jwt = require('jsonwebtoken');
// Decode without verification
const decoded = jwt.decode(token, { complete: true });
console.log(decoded.header); // { alg: 'RS256', typ: 'JWT' }
console.log(decoded.payload); // { sub: '123', name: 'Alice', ... }
// Verify and decode
try {
const verified = jwt.verify(token, publicKey);
} catch (err) {
console.log('Invalid:', err.message);
}
Python (PyJWT)
import jwt
# Decode without verification
payload = jwt.decode(token, options={"verify_signature": False})
# Verify and decode
try:
payload = jwt.decode(token, public_key, algorithms=["RS256"])
except jwt.ExpiredSignatureError:
print("Token expired")
except jwt.InvalidTokenError:
print("Invalid token")
Go
import "github.com/golang-jwt/jwt/v5"
token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return publicKey, nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
fmt.Println(claims["sub"])
}
JWT Security Best Practices
- Always verify signatures. Decoding ≠ verifying. Anyone can create a JWT with any payload.
- Use short expiration times. 15-60 minutes for access tokens. Use refresh tokens for longer sessions.
- Don't store sensitive data in JWTs. The payload is only Base64-encoded, not encrypted. Anyone can decode it.
- Validate
issandaudclaims. Prevent tokens from one service being used in another. - Use asymmetric algorithms (RS256, ES256) in distributed systems so services can verify without knowing the signing key.
- Reject
alg: "none". Always. - Rotate signing keys regularly. Use JWKS (JSON Web Key Set) for key rotation.
JWT vs Session Cookies vs API Keys
| Feature | JWT | Session Cookie | API Key |
|---|---|---|---|
| Stateless | Yes | No (server stores session) | Yes |
| Self-contained | Yes (claims inside) | No (ID only) | No |
| Expiration | Built-in (exp) | Server-controlled | Manual |
| Revocation | Hard (need blocklist) | Easy (delete server-side) | Easy (delete key) |
| Size | ~800 bytes typical | ~32 bytes (session ID) | ~32-64 bytes |
| Cross-domain | Easy (Authorization header) | Hard (CORS, SameSite) | Easy |
Try It Free
Paste any JWT and instantly see the decoded header, payload, and expiration status. Your token never leaves your browser.
🔑 Free JWT Decoder
Decode, inspect, and debug JSON Web Tokens — all client-side, all free.
Open JWT Decoder →More Free Dev Tools
- JSON Formatter — validate and pretty-print JSON
- Base64 Encoder/Decoder — encode and decode Base64
- Hash Generator — MD5, SHA-256, SHA-512
- Password Generator — cryptographically secure passwords
- View all 21+ free tools →