JWT Decoder
Paste any JWT token to decode and inspect its header, payload claims and signature. Timestamps are converted to human-readable dates and expiration is checked automatically. Your token never leaves the browser.
{
"alg": "HS256",
"typ": "JWT"
}sub"usr_devessentials"
Subject — who the token refers to
name"Developer"
Full name
email"hello@devessentials.dev"
Email address
iss"devessentials.dev"
Issuer — who issued the token
aud"api.devessentials.dev"
Audience — who the token is intended for
iat3/27/2025, 12:00:00 AM(1743033600)
Issued At — when the token was issued
exp3/31/2026, 11:33:20 PM(1775000000)
Expiration — when the token expires
role"admin"
User role
plan"pro"
Raw JSON
{
"sub": "usr_devessentials",
"name": "Developer",
"email": "hello@devessentials.dev",
"iss": "devessentials.dev",
"aud": "api.devessentials.dev",
"iat": 1743033600,
"exp": 1775000000,
"role": "admin",
"plan": "pro"
}SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Signature verification requires the secret key and cannot be done client-side securely.
Decoding JWTs in Code
JavaScript (Browser) — Manual Decode
// JWTs are Base64URL-encoded — no library needed to read claims
function decodeJwt(token) {
const [header, payload] = token.split(".");
const decode = (str) =>
JSON.parse(atob(str.replace(/-/g, "+").replace(/_/g, "/")));
return { header: decode(header), payload: decode(payload) };
}
const { header, payload } = decodeJwt("eyJhbGciOiJIUzI1NiJ9...");
console.log(payload.sub); // user ID
console.log(payload.exp); // expiry (Unix timestamp)
// ⚠️ This only reads claims — it does NOT verify the signature.Node.js — jose (verify + decode)
// npm install jose
import { jwtVerify, decodeJwt } from "jose";
// Decode without verification (inspect claims only)
const claims = decodeJwt("eyJhbGciOiJIUzI1NiJ9...");
console.log(claims);
// Verify HS256 token
const secret = new TextEncoder().encode("your-secret-key");
const { payload } = await jwtVerify("eyJhbGciOiJIUzI1NiJ9...", secret);
console.log(payload.sub);
// Verify RS256 token using JWKS endpoint
import { createRemoteJWKSet } from "jose";
const JWKS = createRemoteJWKSet(new URL("https://auth.example.com/.well-known/jwks.json"));
const { payload: p } = await jwtVerify(token, JWKS);Python — PyJWT
# pip install PyJWT cryptography
import jwt
# Decode without verification (inspect claims only)
claims = jwt.decode(token, options={"verify_signature": False})
print(claims)
# Verify HS256 token
claims = jwt.decode(token, key="your-secret-key", algorithms=["HS256"])
print(claims["sub"])
# Verify RS256 token
with open("public_key.pem") as f:
public_key = f.read()
claims = jwt.decode(token, public_key, algorithms=["RS256"])Go — golang-jwt
// go get github.com/golang-jwt/jwt/v5
import "github.com/golang-jwt/jwt/v5"
// Parse and verify HS256
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte("your-secret-key"), nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
fmt.Println(claims["sub"])
fmt.Println(claims["exp"])
}Terminal
# Decode payload (no verification) using base64 and jq
TOKEN="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImV4cCI6MTcwMDAwMDAwMH0.sig"
PAYLOAD=$(echo $TOKEN | cut -d. -f2)
# Add padding if needed and decode
echo "${PAYLOAD}==" | tr '_-' '/+' | base64 -d | jq .
# Output:
# { "sub": "user_123", "exp": 1700000000 }JWT Structure Explained
A JWT (JSON Web Token) is three Base64URL-encoded segments separated by dots: header.payload.signature
| Part | Contains | Example |
|---|---|---|
| Header | Algorithm and token type | { "alg": "HS256", "typ": "JWT" } |
| Payload | Claims (user data, expiry) | { "sub": "user_123", "exp": 1712686400 } |
| Signature | HMAC or RSA of header+payload | Verified server-side — cannot be faked without the secret |
Standard claims in the payload: sub (subject — user ID), iss (issuer), aud (audience), iat (issued at — Unix timestamp), exp (expiry — Unix timestamp), nbf (not before). Any additional claims (role, email, permissions) are custom and defined by the application.
Decoding ≠ verifying. This tool decodes the payload — it reads the claims but does not check the signature. Anyone can decode any JWT. Only your server, using the correct secret or public key, can verify that it hasn't been tampered with.
Related Tools
How JWT Authentication Works
JWT structure, signing algorithms, HS256 vs RS256, and security best practices.
Base64 Encoder/Decoder
JWT uses Base64URL encoding internally.
Hash Generator
SHA-256, HMAC and more.
Password Generator
Generate secure signing secrets.
Why You Should Never Paste JWTs Into Online Tools
The real risk of server-side JWT decoders.
Frequently Asked Questions
What is the difference between decoding a JWT and verifying it?▾
Decoding a JWT just Base64URL-decodes the header and payload — it reads the claims but performs zero cryptographic checks. Anyone can decode any JWT without a secret. Verification confirms the signature using the correct algorithm and key, ensuring the token was issued by a trusted party and hasn't been tampered with. This tool is for debugging and inspection only — your server code must always verify the signature before trusting any claim inside a JWT.
What is the 'alg: none' JWT vulnerability and how does it work?▾
The JWT spec originally allowed "alg": "none" to indicate an unsigned token. Several JWT libraries had bugs where passing alg: none in the header caused them to skip signature verification entirely, accepting any forged token as valid. A 2015 disclosure showed this affected multiple popular libraries across languages. Always configure your JWT library to explicitly whitelist the expected algorithm(s) and reject none — never trust the algorithm specified in the token header without validation.
What is the difference between HS256 and RS256, and when should I use each?▾
HS256 (HMAC-SHA256) uses a single shared secret — the same key signs and verifies the token, so every service that needs to verify tokens must possess the secret. RS256 (RSA-SHA256) uses a private key to sign and a public key to verify — you can distribute the public key freely via a JWKS endpoint so any service can verify tokens without ever seeing the signing key. Use RS256 (or ES256) when multiple independent services need to verify tokens, or when integrating with third-party identity systems.
What causes 'token expired' errors on a freshly issued JWT?▾
The exp claim is a Unix timestamp (seconds since epoch) checked against the validating server's current time. A common cause of unexpected expiry errors is clock skew between the issuing and validating servers — even a 1–2 minute difference will cause a freshly issued token to appear expired. Most JWT libraries allow configuring a 'leeway' (e.g., 60 seconds) to tolerate minor clock drift. A decoded token that looks valid in a browser debugger may still be rejected server-side due to this timing mismatch.
Why do the three parts of a JWT use Base64URL encoding instead of standard Base64?▾
Standard Base64 uses + and / characters, which have special meaning in URLs (+ = space, / = path separator) and would require percent-encoding if the JWT were passed as a URL parameter or cookie. Base64URL (RFC 4648 §5) substitutes - for + and _ for /, and omits the = padding characters, producing a compact token that is safe in HTTP headers, query strings, and cookies without any further encoding. This is why the three dot-separated segments of a JWT never contain those characters.