How to Decode a JWT Token Online

JWT tokens are everywhere in modern auth flows — but they look like gibberish until you decode them. This guide explains what's inside a JWT, how to read it, and when decoding is useful for debugging.

What is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe string used to represent claims between two parties. In practice, you'll encounter them as authentication tokens: your app logs in, the server returns a JWT, and you send that token in the Authorization header on every subsequent request.

A JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGljZSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcwNTMxMjIwMCwiZXhwIjoxNzA1Mzk4NjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three base64url-encoded parts, separated by dots. They are not encrypted — they are signed. Anyone can decode and read the contents. The signature only proves the token hasn't been tampered with.

The three parts of a JWT

Part 1: Header

The first segment contains metadata about the token itself — specifically, which signing algorithm was used.

// Encoded:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

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

Common values for alg: HS256 (HMAC-SHA256, symmetric),RS256 (RSA-SHA256, asymmetric), ES256 (ECDSA). If you're integrating with an OAuth provider, you'll often see RS256.

Part 2: Payload

The payload contains the actual claims — data the token is asserting. There are registered claim names with well-known meanings, plus custom claims your app defines.

// Encoded:
eyJzdWIiOiJ1c2VyXzEyMyIsIm5hbWUiOiJBbGljZSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcwNTMxMjIwMCwiZXhwIjoxNzA1Mzk4NjAwfQ

// Decoded:
{
  "sub": "user_123",
  "name": "Alice",
  "role": "admin",
  "iat": 1705312200,
  "exp": 1705398600
}

Standard registered claims:

  • sub — subject: the user or entity the token refers to
  • iat — issued at: Unix timestamp when the token was created
  • exp — expiration: Unix timestamp after which the token is invalid
  • iss — issuer: who created the token
  • aud — audience: who the token is intended for
  • jti — JWT ID: a unique identifier for the token (used to prevent replay)

Part 3: Signature

The signature is computed by hashing the encoded header + encoded payload with a secret key (for HS256) or a private key (for RS256/ES256). The server validates the signature on every request to confirm the token is authentic and unmodified.

Decoding a JWT does not verify the signature — that requires the secret or public key. Decoding only unpacks the base64url encoding to reveal the JSON contents.

When to decode a JWT

Debugging authentication errors

Getting a 401 on an authenticated request? Decode the token and check:

  • Is exp in the past? The token expired.
  • Is aud set to the wrong service? The token is for a different API.
  • Is sub what you expect? You might be using a token from the wrong user.
  • Is the role or permission claim missing? Your auth middleware might not have included it.

Checking expiry

The exp claim is a Unix timestamp (seconds since epoch). To convert it manually:

// exp: 1705398600
new Date(1705398600 * 1000).toISOString()
// → "2024-01-16T10:30:00.000Z"

A good JWT decoder shows this as a human-readable date automatically, saving you the conversion.

Inspecting claims from OAuth providers

When integrating Google, Auth0, Okta, or another OAuth provider, the ID token or access token contains claims your app needs (email, name, roles, scopes). Decoding lets you verify the provider is sending exactly what you expect before writing the code to consume it.

Security: never paste production tokens into untrusted tools

This is important. A JWT issued in production is a valid credential. Pasting it into a random online tool means:

  • The tool's server might log it
  • The payload (including user data) is now on someone else's system
  • If the token hasn't expired, it can be replayed

For production tokens, either decode them locally in your browser's DevTools console —atob(token.split('.')[1]) — or use a tool that explicitly processes everything client-side with no server uploads.

Jsonic's JWT decoder runs entirely in your browser. Nothing is sent to any server. That makes it safe to use for inspecting tokens during development and staging — though for sensitive production tokens, local decoding is always the right default.

Decoding a JWT manually

You can decode a JWT in any browser console without a tool:

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyJ9.signature'
const [header, payload] = token.split('.')
JSON.parse(atob(header))   // → { alg: 'HS256', typ: 'JWT' }
JSON.parse(atob(payload))  // → { sub: 'user_123' }

Note: atob expects standard base64, but JWTs use base64url (uses -and _ instead of + and /). For most tokens this doesn't matter, but strictly you should replace those characters first:

function decodeJwtPart(part) {
  const base64 = part.replace(/-/g, '+').replace(/_/g, '/')
  return JSON.parse(atob(base64))
}

Decode a JWT now

Paste any JWT token into Jsonic's JWT Decoder to instantly see the header, payload, and expiry time formatted as human-readable JSON and dates. No server. No uploads.

Open JWT Decoder