JWT Decoder

JWT Token

Last updated:

Jsonic's JWT Decoder splits a JWT token into its three parts and decodes the header and payload from Base64url encoding. The header typically contains the signing algorithm (alg) and token type (typ). The payload contains claims such as sub, iat, and exp. The signature is displayed but not cryptographically verified — that requires the secret key.

A JWT has three dot-separated parts

A JSON Web Token is a single compact string with three base64url-encoded segments joined by dots: header.payload.signature. The first two segments are JSON objects; the third is a binary signature. They are not encrypted — they are signed, so anyone holding the token can read the header and payload.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcwNTMxMjIwMCwiZXhwIjoxNzA1Mzk4NjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└──────────── header ───────────┘ └──────────────────── payload ────────────────────┘ └──────────── signature ───────────┘

Decode each segment with base64url

The header and payload are base64url-encoded, not standard base64: they use -and _ instead of + and /, and drop the trailing= padding. Decoding the example above gives:

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

// payload
{
  "sub": "user_123",
  "role": "admin",
  "iat": 1705312200,
  "exp": 1705398600
}

If you only have a base64 string and want to inspect it on its own, use the Base64 encoder/decoder. For a full walkthrough of the JWT format, see the guide to decoding a JWT.

Standard payload claims worth knowing

The payload carries claims — assertions about the user or session. Six registered claims (defined in RFC 7519) cover almost every debugging scenario:

  • sub — subject: the user or entity the token is about
  • iat — issued at: Unix timestamp when the token was created
  • exp — expiration: Unix timestamp after which the token is invalid
  • iss — issuer: who minted the token (e.g. your auth server or Auth0)
  • aud — audience: which service the token is intended for
  • nbf — not before: a time before which the token must be rejected

When an authenticated request returns a 401, decode the token and check these first: anexp in the past means it expired, a wrong aud means it belongs to a different API, and an unexpected sub means you grabbed the wrong user's token.

Security: the browser only decodes, it never verifies

This is the single most important thing to understand about decoding. Unpacking the base64url encoding tells you what the token claims, but proves nothing about who issued it or whether it was tampered with. Verification needs a key the browser does not have — the shared secret for HS256, or the issuer's public key forRS256/ES256 — and must run server-side.

  • Never make an authorization decision from a payload you only decoded.
  • A production JWT is a live credential — an untrusted online tool may log it and replay it before exp.
  • Anyone with the token can read the payload, so never put secrets in it.

Jsonic's decoder runs entirely client-side — your token never leaves the browser — which makes it safe for development and staging tokens. For sensitive production tokens, decoding locally in DevTools (atob(token.split('.')[1])) is always the safer default.

Decode a JWT in code

When you need decoding inside a script or backend instead of the browser tool:

# Python — PyJWT, decode without verifying the signature
import jwt
claims = jwt.decode(token, options={"verify_signature": False})
print(claims)  # {'sub': 'user_123', 'role': 'admin', 'iat': ..., 'exp': ...}
// Node.js — jsonwebtoken, decode (does NOT verify)
const jwt = require('jsonwebtoken')
const decoded = jwt.decode(token, { complete: true })
console.log(decoded.header)   // { alg: 'HS256', typ: 'JWT' }
console.log(decoded.payload)  // { sub: 'user_123', exp: 1705398600, ... }
// Pure JS — no dependency, base64url by hand
function decodeJwtPart(part) {
  const base64 = part.replace(/-/g, '+').replace(/_/g, '/')
  return JSON.parse(atob(base64))
}
const [header, payload] = token.split('.')
decodeJwtPart(header)   // → { alg: 'HS256', typ: 'JWT' }
decodeJwtPart(payload)  // → { sub: 'user_123', ... }

Checking expiry from the exp claim

The exp claim is a Unix timestamp in seconds, not milliseconds — a common off-by-1000 bug. Convert it to a date with new Date(exp * 1000) and compare against Date.now() / 1000 to decide whether the token has expired.

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

const isExpired = Date.now() / 1000 > payload.exp

This decoder shows exp (and iat) as human-readable dates automatically. For the deeper background — algorithm comparison, OAuth provider tokens, and when to verify instead of decode — read the how to decode a JWT guide.

How to decode a JWT token

  1. Paste your JWT token into the input field.
  2. Click Decode.
  3. The header and payload are shown as formatted JSON.
  4. The exp and iat claims are shown as human-readable dates; the signature is displayed but not verified.

FAQ

Does this verify the JWT signature?

No. Signature verification requires the secret key. This tool only decodes and displays the header and payload.

Is my token sent to a server?

No. Decoding runs entirely in your browser. Your token never leaves your machine.

What is in the JWT header?

The header contains the algorithm (alg) and token type (typ), e.g. {"alg": "HS256", "typ": "JWT"}.

What is in the JWT payload?

The payload contains claims — typically sub (subject), iat (issued at), exp (expiration), and any custom fields set by the issuer.

How do I check if a JWT is expired?

Look for the exp claim in the payload. It is a Unix timestamp (seconds since 1970-01-01). Compare it to the current time. This tool displays exp as a human-readable date.

What is the difference between HS256 and RS256?

HS256 uses a shared secret for signing and verification. RS256 uses a private key to sign and a public key to verify — common in OAuth 2.0 and OpenID Connect.

Can I decode any JWT format?

This tool decodes standard three-part JWTs (header.payload.signature). It does not support JWE (encrypted tokens).