How to Decode a JWT Token (Without a Library)

Independently researched No sponsored picks Affiliate supported

JWT (JSON Web Token) is the most widely used token format for authentication on the web. If you’ve ever worked with OAuth, API keys, or session tokens, you’ve almost certainly encountered a JWT. Here’s how to decode one, understand what’s inside, and avoid the security pitfalls that catch even experienced developers.

What Is a JWT?

A JWT is a compact, URL-safe string that carries a JSON payload. It looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It’s three Base64URL-encoded segments separated by dots:

HEADER.PAYLOAD.SIGNATURE

Each part has a specific purpose:

Part Contains Purpose
Header Algorithm + token type Tells the server how to verify the signature
Payload Claims (user data, expiration, etc.) Carries the actual information
Signature Cryptographic hash Proves the token hasn’t been tampered with

How to Decode a JWT (Step by Step)

Method 1: Use the Online Decoder (Fastest)

  1. Open our JWT Decoder
  2. Paste your JWT into the input field
  3. Instantly see the decoded header, payload, and signature status
  4. Check expiration time, issuer, and all claims at a glance

The decoder runs entirely in your browser — your token is never sent to any server.

Method 2: Decode Manually (No Tools Needed)

Since a JWT is just Base64URL-encoded JSON, you can decode it with any Base64 decoder or even from the command line.

Step 1: Split the token by dots

Part 1 (Header):  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Part 2 (Payload): eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNjE2MjM5MDIyfQ
Part 3 (Signature): SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Step 2: Base64URL-decode each part

Base64URL is slightly different from standard Base64:

  • - replaces +
  • _ replaces /
  • No = padding

You can use our Base64 Encoder/Decoder or the command line:

echo 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' | base64 -d

Step 3: Read the JSON

Decoded header:

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

Decoded payload:

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

The signature is a binary hash — you can’t “read” it, but you can verify it with the correct secret key.

Method 3: Decode in JavaScript (One Line)

JSON.parse(atob(token.split('.')[1]))

This decodes the payload directly. Note: atob() handles standard Base64 but may fail on Base64URL characters. For production code, add padding and character replacement:

function decodeJWT(token) {
  const payload = token.split('.')[1];
  const base64 = payload.replace(/-/g, '+').replace(/_/g, '/');
  return JSON.parse(atob(base64));
}

Method 4: Decode in Python

import base64
import json

token = "eyJhbGciOiJIUzI1NiIs..."
payload = token.split('.')[1]
# Add padding
payload += '=' * (4 - len(payload) % 4)
decoded = json.loads(base64.urlsafe_b64decode(payload))
print(decoded)

Understanding the JWT Header

The header is typically small and contains two fields:

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

Common Algorithms

Algorithm Type Key Security Level
HS256 Symmetric Shared secret Good for internal services
HS384 Symmetric Shared secret Stronger variant of HS256
HS512 Symmetric Shared secret Strongest symmetric option
RS256 Asymmetric RSA public/private key pair Standard for public APIs
RS512 Asymmetric RSA public/private key pair Higher security RSA
ES256 Asymmetric ECDSA key pair Compact, fast, modern
EdDSA Asymmetric Ed25519 key pair Cutting-edge, fastest
none None No signature NEVER use in production

Key takeaway: If you see "alg": "none" in a JWT, it means the token is unsigned and can be forged by anyone. This is a critical security vulnerability.

Understanding the JWT Payload

The payload contains claims — statements about the user and the token itself. There are three types:

Registered Claims (Standard)

These are predefined by the JWT specification (RFC 7519):

Claim Full Name Description Example
sub Subject Who the token is about (usually user ID) "1234567890"
iss Issuer Who created the token "auth.example.com"
aud Audience Who the token is for "api.example.com"
exp Expiration When the token expires (Unix timestamp) 1716239022
iat Issued At When the token was created 1616239022
nbf Not Before Token is not valid before this time 1616239022
jti JWT ID Unique identifier for the token "a1b2c3d4"

Public Claims

Custom claims registered with IANA or using collision-resistant names:

{
  "email": "[email protected]",
  "name": "Alice",
  "role": "admin"
}

Private Claims

Custom claims agreed upon between parties:

{
  "team_id": "engineering",
  "permissions": ["read", "write", "deploy"]
}

Understanding the JWT Signature

The signature is created by combining the encoded header and payload with a secret:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

Important: You can always decode a JWT without the secret. But you can only verify it with the secret (or public key for asymmetric algorithms). Decoding tells you what’s inside. Verification tells you whether to trust it.

How to Check if a JWT Is Expired

The exp claim is a Unix timestamp. To check expiration:

In JavaScript:

const payload = JSON.parse(atob(token.split('.')[1]));
const isExpired = payload.exp * 1000 < Date.now();
console.log(isExpired ? 'Token expired' : 'Token valid');

Using the online decoder: Our JWT Decoder automatically converts timestamps to human-readable dates and flags expired tokens.

Quick manual check: Paste the exp value into any Unix timestamp converter. If the date is in the past, the token is expired.

JWT Security Best Practices

Do Not Store Sensitive Data in the Payload

JWTs are encoded, not encrypted. Anyone can decode the payload. Never put passwords, credit card numbers, or secret keys in a JWT.

Always Verify the Signature

Decoding is not the same as verifying. Your server must verify the signature before trusting any claims. Use a proper JWT library for your language — don’t roll your own verification.

Set Short Expiration Times

Use Case Recommended exp
Access tokens (API calls) 15 minutes to 1 hour
Refresh tokens 7-30 days
Email verification links 24 hours
Password reset tokens 15-60 minutes

Validate the alg Header

Your server should whitelist allowed algorithms. A classic attack (CVE-2015-2951) tricks servers into accepting "alg": "none", bypassing signature verification entirely. Always enforce the expected algorithm.

Use HTTPS Only

JWTs sent over HTTP can be intercepted. Always transmit tokens over HTTPS and set the Secure flag on cookies.

Implement Token Revocation

JWTs are stateless — there’s no built-in way to revoke them. For sensitive systems, maintain a blocklist of revoked token IDs (jti) or use short-lived tokens with refresh rotation.

JWT vs Session Cookies vs API Keys

Feature JWT Session Cookie API Key
Stateless Yes No (server stores session) Depends
Scalable Excellent (no server storage) Harder (shared session store) Excellent
Revocable Difficult (needs blocklist) Easy (delete session) Easy (delete key)
Cross-domain Yes (sent in headers) Limited (same-site) Yes
Payload Rich (carries claims) None (just an ID) None
Expiration Built-in (exp claim) Server-controlled Usually none
Best for SPAs, mobile apps, microservices Traditional web apps Server-to-server

Debugging JWT Issues

When authentication fails, decode the JWT first to check:

  1. Is it expired? Check the exp claim against the current time
  2. Is the audience correct? The aud claim must match your API’s expected value
  3. Is the issuer correct? The iss claim must match your auth server
  4. Is the algorithm expected? A mismatch means verification will fail
  5. Is the token malformed? It should have exactly three dot-separated parts

Our JWT Decoder highlights all of these issues automatically, making it the fastest way to debug token problems.

Frequently Asked Questions

Is it safe to decode a JWT in the browser?

Yes. Decoding a JWT only reveals the payload — it doesn’t compromise security because the signature can’t be forged without the secret key. Our JWT Decoder runs entirely client-side, so your tokens never leave your browser.

Can I modify a JWT and reuse it?

No. If you change any part of the header or payload, the signature becomes invalid. The server will reject the modified token (assuming proper signature verification).

What’s the difference between decoding and verifying a JWT?

Decoding extracts the header and payload — anyone can do this. Verifying checks the signature against the secret key to confirm the token hasn’t been tampered with. Always verify on the server side.

Why is my JWT so long?

JWT size grows with the number of claims. Every claim adds to the payload, which adds to the Base64 string. Keep payloads lean — store only essential claims and look up additional data server-side.

Can JWTs be encrypted?

Yes. JWE (JSON Web Encryption) encrypts the payload so it can’t be read without a decryption key. Standard JWTs (JWS) are only signed, not encrypted — the payload is visible to anyone who decodes it.

How do I decode a JWT from an HTTP header?

Authorization headers typically use the format Bearer <token>. Strip the “Bearer " prefix, then decode the remaining string. In code: const token = authHeader.replace('Bearer ', '').

Why Trust FindPicked?

Our recommendations are based on extensive research, real user reviews, and spec-by-spec analysis. We never accept payment for placement. When you buy through our links, we may earn a commission — this supports our work at no extra cost to you.

Learn how we pick →