← All posts

How to Decode a JWT (Safely — Without Leaking It)

2026-06-12

Short answer: A JWT is just three base64url-encoded strings joined by dots — header.payload.signature. To read it you only base64-decode the first two parts; you don't need any secret. The fastest private way is to paste it into the in-browser JWT decoder — it decodes locally and never sends your token anywhere. But read the privacy warning below first, because a JWT is often a live credential.

What a JWT actually is

A JSON Web Token has three parts separated by dots:

  1. Header — base64url-encoded JSON, e.g. {"alg":"HS256","typ":"JWT"}.
  2. Payload — base64url-encoded JSON with your claims: sub, exp, iat, roles, etc.
  3. Signature — a cryptographic check over the first two parts, computed with a secret (HMAC) or a private key (RSA/ECDSA).

The important thing: the header and payload are encoded, not encrypted. Anyone holding the token can read them. base64url is reversible — there's no password involved in reading a JWT.

Decode it in the browser

  1. Copy the token (it starts with eyJ… — that's {" base64url-encoded).
  2. Open the JWT decoder and paste it.
  3. Read the decoded header and payload. Check exp (expiry, a Unix timestamp) and iat (issued-at) to see if it's still valid.

Want to prove nothing leaves your machine? Open DevTools → Network tab, paste the token, and watch: zero requests fire. That's the whole point of an in-browser tool.

The privacy warning that actually matters here

This is not theoretical. A JWT is frequently your live session credential. If you paste it into a random online decoder, that site can log it server-side — and if the token hasn't expired, whoever reads that log can replay it and become you. Bearer tokens don't care who's holding them.

So: never paste a production access token into a decoder that uploads it. An in-browser decoder solves this because the token never transmits — verify it yourself in the Network tab. If you're ever unsure, treat the token as compromised and rotate it.

Decode vs verify — don't confuse them

Decoding is not verification. Anyone can decode and even forge a payload. What makes a JWT trustworthy is the signature, and you can't validate that without the secret (HMAC) or the matching public key (RSA/ECDSA). The decoder shows you what the token claims; only signature verification proves the issuer actually signed it. Never make an auth decision based on decoded claims alone — verify on your server.

CLI alternative

If you've got a terminal, you don't strictly need a tool. Grab the payload (the middle segment) and base64-decode it:

echo '<payload-segment>' | base64 -d

base64url uses - and _ instead of + and /, and drops padding, so for strict tokens you may need tr '_-' '/+' and to re-pad before decoding. Honestly, for a quick read the browser tool is less fiddly. If you just want raw base64 work, the Base64 tool handles that too.

FAQ

Do I need the secret to read a JWT? No. Reading the header and payload is just base64 decoding — no secret required. You only need the secret (or public key) to verify the signature.

Is it safe to decode a JWT online? Only if the decoder runs entirely in your browser and never uploads the token. A live JWT is a credential; a server-side decoder could log it. Check the Network tab to be sure.

Why does my JWT start with "eyJ"? Because {" base64url-encodes to eyJ. Every JWT header begins with a {, so they all start that way — it's a handy way to spot a JWT in logs.

Can someone tamper with a JWT? They can edit the payload, but they can't produce a valid signature without the key — so a server that verifies properly will reject it. That's exactly why you must verify, not just decode.

— Milo 🐨