How to Read a JWT Token

Step-by-step guide to decoding and reading a JWT token. Learn how to extract the header, payload, and claims — and what you can trust without verifying the signature.

Decode a JWT in 3 Steps

A JWT has three Base64url-encoded sections separated by dots. You can read the header and payload without any secret key — paste the token to decode it instantly.

  1. Identify the three parts: Split the token on the . character — you get header.payload.signature. The header and payload are readable; the signature is binary.
  2. Base64url-decode the header and payload: Each section is Base64url-encoded JSON. Decoding reveals the algorithm used and the claims stored in the token.
  3. Read the claims: The payload contains key-value pairs — sub (user ID), exp (expiry), iat (issued at), and any custom claims your app added.

Reading a JWT Without a Tool

  • In the browser console: JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/'))) — decodes the payload to a JavaScript object
  • In Python: import base64, json; json.loads(base64.b64decode(token.split('.')[1] + '==')) — the == adds padding that Base64url sometimes omits
  • Command line: echo "PAYLOAD" | base64 -d — replace PAYLOAD with the middle section of the JWT (add = padding if needed)
  • Why the replace calls: JWT uses Base64url (- and _ instead of + and /). Standard atob() and base64 -d expect standard Base64, so you convert the characters first

What You Can and Cannot Trust

  • You CAN read without a key: The payload is encoded, not encrypted — any Base64url decode reveals the claims. This is by design: the claims are meant to be readable
  • You CANNOT trust without verifying: Anyone can craft a JWT with any payload. Without verifying the signature against a known secret or public key, you have no proof the token was issued by a trusted party
  • Safe for debugging: Decoding a JWT to inspect its claims during development is perfectly fine — you're just reading, not trusting
  • Never skip verification in production: Always verify the signature server-side before acting on JWT claims in production code. Use a library — jsonwebtoken (Node.js), PyJWT (Python), golang-jwt/jwt (Go)
  • Check exp even after verification: A valid signature doesn't mean the token isn't expired — libraries handle this, but confirm your verification call checks expiry

Frequently Asked Questions

Why does my JWT payload look garbled when I base64 decode it?

JWT uses Base64url encoding, which replaces + with - and / with _, and often omits the = padding characters. Standard Base64 decoders expect +, /, and padding. To fix: replace -+ and _/ in the section, then add = padding to make the length a multiple of 4, then decode. The JWT decoder on this page handles all of this automatically.

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

Decoding reads the claims by Base64url-decoding the payload — anyone can do this with no key. Verifying checks that the signature matches the header + payload using the secret or public key — only someone with the correct key can verify. In production, you must always verify before trusting claims. Decoding without verification is useful for debugging (inspecting what a token contains) but must never be used to make authorization decisions.

How do I check if a JWT is expired without a library?

Decode the payload and read the exp claim — it's a Unix timestamp (seconds since epoch). Compare to the current time: Date.now() / 1000 > payload.exp in JavaScript returns true if expired. Full one-liner: JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/'))).exp * 1000 < Date.now(). Remember: this tells you what the token claims about its expiry — only signature verification confirms the claim is trustworthy.