X Xerobit

JWT Token Decoder — Decode and Inspect JWT Tokens Online

A JWT decoder reveals the header, payload, and signature of any JSON Web Token without needing the secret key. Here's how JWT decoding works and what each part of a token contains.

Mian Ali Khalid · · 5 min read
Use the tool
JWT Decoder
Decode and inspect JSON Web Tokens. Local-only — tokens never leave your browser.
Open JWT Decoder →

A JWT (JSON Web Token) decoder extracts the header and payload from a JWT string, displaying them as readable JSON. Anyone can decode a JWT — no secret key required. The encoding is just Base64URL, not encryption.

Use the JWT Decoder to inspect token headers, payloads, and expiration times without revealing your secret key.

What a JWT looks like

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzE1NDE0NDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three base64url-encoded parts separated by dots:

  1. Header — algorithm and token type
  2. Payload — claims (user data, expiration, etc.)
  3. Signature — HMAC or RSA signature (verifies authenticity)

Decoding step by step

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Base64URL decode → JSON:

{
  "alg": "HS256",
  "typ": "JWT"
}

alg specifies the signing algorithm. Common values:

  • HS256 — HMAC-SHA256 (symmetric — same secret for signing and verifying)
  • RS256 — RSA-SHA256 (asymmetric — private key signs, public key verifies)
  • ES256 — ECDSA-SHA256 (asymmetric — elliptic curve variant)
  • nonedangerous — no signature, token can be forged

Payload

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNzE1NDE0NDAwfQ

Base64URL decode → JSON:

{
  "sub": "1234567890",
  "name": "Alice",
  "iat": 1715414400
}

The payload contains claims — assertions about the user or the token itself.

Standard claims

ClaimNameMeaning
issIssuerWho issued the token
subSubjectWho the token is about (usually user ID)
audAudienceWho the token is intended for
expExpirationUnix timestamp when the token expires
nbfNot BeforeToken invalid before this timestamp
iatIssued AtWhen the token was issued
jtiJWT IDUnique identifier for this token

exp is the most important claim for debugging. If a token is being rejected, check if it’s expired. The JWT Decoder shows the expiration as a human-readable date.

Custom claims

Any additional fields are custom claims added by the application:

{
  "sub": "user_123",
  "email": "alice@example.com",
  "roles": ["admin", "editor"],
  "org_id": "org_456",
  "plan": "pro",
  "exp": 1746950400
}

Custom claims let you embed user metadata in the token so the server doesn’t need to query the database on every request.

Decoding in code

JavaScript

// Decode without verification (inspection only):
function decodeJwt(token) {
  const [header, payload] = token.split('.').slice(0, 2);
  
  const decode = (str) => {
    // Add padding for base64 decoding:
    const padded = str.padEnd(str.length + ((4 - (str.length % 4)) % 4), '=');
    return JSON.parse(atob(padded.replace(/-/g, '+').replace(/_/g, '/')));
  };
  
  return {
    header: decode(header),
    payload: decode(payload)
  };
}

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
const { header, payload } = decodeJwt(token);
console.log(payload.exp);  // Unix timestamp
console.log(new Date(payload.exp * 1000));  // Human-readable expiration

Node.js (jsonwebtoken — with verification)

const jwt = require('jsonwebtoken');

// Decode without verification:
const decoded = jwt.decode(token);
console.log(decoded);

// Verify AND decode (production use):
try {
  const verified = jwt.verify(token, process.env.JWT_SECRET);
  console.log(verified.sub, verified.email);
} catch (err) {
  if (err.name === 'TokenExpiredError') {
    console.log('Token expired at:', err.expiredAt);
  }
}

Python

import base64
import json

def decode_jwt(token):
    parts = token.split('.')
    
    def b64_decode(s):
        # Add padding:
        padding = 4 - len(s) % 4
        s += '=' * (padding % 4)
        # URL-safe base64:
        return json.loads(base64.urlsafe_b64decode(s))
    
    return {
        'header': b64_decode(parts[0]),
        'payload': b64_decode(parts[1])
    }

# With PyJWT for verification:
import jwt  # pip install PyJWT

# Decode without verification:
decoded = jwt.decode(token, options={"verify_signature": False})

# Verify (production use):
try:
    payload = jwt.decode(token, secret_key, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
    print("Token has expired")
except jwt.InvalidTokenError:
    print("Invalid token")

What decoding can’t tell you

Decoding ≠ verification. Anyone can decode a JWT to read the payload. The signature verifies that the token was issued by someone with the secret key and hasn’t been tampered with.

Decoding is safe for:

  • Debugging what’s in a token
  • Reading the expiration time
  • Inspecting claims in development

You must verify before:

  • Trusting any claim (user ID, roles, permissions)
  • Granting access to any resource
  • Using the token in any production decision

Never trust an unverified JWT payload. An attacker can craft a token with any payload they want — the signature will be invalid, but the decoded claims will look real.

Inspecting JWTs in the browser

In Chrome DevTools, if your API uses JWT:

  1. Network tab → find an API request
  2. Click the request → Headers → Authorization header
  3. Copy the value after “Bearer ”
  4. Paste into the JWT Decoder

This is useful for debugging auth issues: verifying that the right token is being sent, checking whether roles/claims are correct, and confirming expiration times.


Related posts

Related tool

JWT Decoder

Decode and inspect JSON Web Tokens. Local-only — tokens never leave your browser.

Written by Mian Ali Khalid. Part of the Encoding & Crypto pillar.