How to Decode and Read JWT Tokens Manually

8 min readAuthentication & Security

Introduction

JWT tokens look like random gibberish, but they're actually just Base64-encoded JSON. You can decode any JWT without a secret key - the payload is readable by design. This guide shows you how to decode JWTs manually using browser DevTools, command-line tools, and our online debugger.

Understanding the Three Parts

A JWT has three parts separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
│                                 │                                                                  │
└── Header (Base64)               └── Payload (Base64)                                               └── Signature

The header and payload are just Base64URL-encoded JSON. You can decode them anywhere. The signature cannot be decoded - it's a cryptographic hash, not encoded data.

Method 1: Browser DevTools

The fastest way - no tools needed, works in any browser:

// Open browser console (F12), paste:

// Split the token
const token = 'your.jwt.token';
const [header, payload] = token.split('.');

// Decode each part
const headerData = JSON.parse(atob(header));
const payloadData = JSON.parse(atob(payload));

console.log('Header:', headerData);
console.log('Payload:', payloadData);

// Check expiration
if (payloadData.exp) {
  const expires = new Date(payloadData.exp * 1000);
  const isExpired = Date.now() > payloadData.exp * 1000;
  console.log('Expires:', expires.toLocaleString());
  console.log('Expired:', isExpired);
}

Note: atob() works for standard Base64. JWTs use Base64URL which replaces+ and /. For production use, replace them first:atob(payload.replace(/-/g, '+').replace(/_/g, '/'))

Method 2: Command Line

# Using bash (macOS/Linux)
echo 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0' | base64 -d

# Using PowerShell
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0'))

# Using Node.js
node -e "console.log(JSON.parse(Buffer.from('eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0', 'base64').toString()))"

Method 3: Online Debugger

For quick inspection without writing code, use ourJWT Debugger. Paste any JWT and instantly see:

  • Decoded header (algorithm, token type)
  • Decoded payload (all claims with human-readable timestamps)
  • Expiration status (expired or valid)
  • Signature verification (if you provide the secret)

What to Look For When Decoding

  • Expiration (exp) - Is the token still valid? Compare exp to current Unix timestamp.
  • Algorithm (alg) - If it says none, the token has no signature protection. Reject it.
  • Issuer (iss) - Does it match your auth server? Prevents token confusion across services.
  • Audience (aud) - Is this token meant for your service?
  • Sensitive data - Are there passwords, API keys, or PII in the payload? (There shouldn't be!)

Quick Reference: Common Claims

ClaimMeaningExample Value
subSubject (user ID)"1234567890"
iatIssued at1516239022
expExpiration time1516242622
issIssuer"auth.example.com"
audAudience"api.example.com"

Key Takeaways

  • JWT payload is Base64-encoded, not encrypted - anyone can read it
  • Use atob() in browser or base64 -d in terminal to decode
  • Always check exp, iss, and aud claims when debugging
  • Decoding is not verification - you need the secret key to verify the signature
  • Use our JWT Debugger for quick, visual token inspection

Related Resources