How to Decode a JWT Token Safely (Without Leaking Your Secret)
JWTs are everywhere in modern auth. Decoding them is easy — doing it without leaking sensitive data takes care.
JSON Web Tokens (JWTs) carry user identity and permission claims in nearly every modern web application. When debugging an auth flow, you need to decode them constantly to check claim values, verify expiration, and confirm the issuer.
The mistake most developers make: they paste production JWTs into the first online JWT decoder they find on Google. Some of those decoders log requests. Some of them ask for your secret to "verify the signature" which means they now have your signing key. Both are security incidents waiting to happen.
Here's how to decode JWTs safely.
What's in a JWT
A JWT has three parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Reading left to right:
- Header (Base64-URL encoded JSON) the algorithm used and the token type
- Payload (Base64-URL encoded JSON) the claims (user ID, expiration, custom data)
- Signature (Base64-URL encoded bytes) the cryptographic signature, only verifiable with the secret/public key
The header and payload are not encrypted they are just encoded. Anyone with the token can read them.
Method 1: Browser-Based JWT Decoder (Safest)
The fastest and safest method: paste the token into a browser-based JWT decoder.
A proper client-side decoder does three things:
- Splits the token on the
.character - Base64-URL decodes the header and payload locally in your browser
- Pretty-prints both as JSON
Your token is decoded entirely in JavaScript on your machine. Nothing is uploaded.
Example. The token above decodes to:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
The signature shows opaque there is no way to verify it without the signing key.
Method 2: Command Line
If you're already in a terminal:
TOKEN="eyJhbGc..."
# Decode header
echo "$TOKEN" | cut -d. -f1 | base64 -d 2>/dev/null
# Decode payload
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null
The 2>/dev/null suppresses padding warnings Base64-URL strings often need padding added before standard base64 -d accepts them.
A cleaner one-liner using jq:
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq
When to use: scripting, automated checks, terminal-only environments.
Method 3: Browser DevTools Console
If the JWT is already in your browser (cookie, localStorage, or a network request):
const token = "eyJhbGc...";
const [header, payload] = token.split('.').slice(0, 2)
.map(part => JSON.parse(atob(part.replace(/-/g, '+').replace(/_/g, '/'))));
console.log(header);
console.log(payload);
When to use: debugging in DevTools, automated tests in the browser console.
What NOT to Do
The dangerous patterns to avoid:
Don't paste JWTs into random online tools without checking
Many online JWT decoders are server-side they upload your token to process it. If the server logs requests (most do, for debugging), your token is sitting in someone's log file.
A production JWT can be replayed by anyone who has it before it expires. If yours is logged on a third-party server, it's effectively compromised.
Don't paste your signing key anywhere ever
Some online JWT tools offer "signature verification" paste your token and your secret, and they'll verify it. The moment you paste your secret, you've handed over the key that signs every token your app issues.
If a tool asks for your secret or public key, close it. Verify signatures locally using your own auth library.
Don't share JWTs in screenshots or chat
Even if you redact part of the token, the structure (header type and algorithm) and any unredacted segments give attackers information. Treat JWTs like passwords.
Don't decode production tokens in untrusted environments
A production JWT for an admin user is the same as the admin's password (until expiration). Don't decode it on a coworker's laptop, in a coffee shop kiosk, or anywhere you wouldn't paste a password.
Verifying Signature vs Decoding
Decoding tells you what's in the token. Signature verification tells you whether the token is genuine. They're different operations.
- Decoding requires no secret anyone can do it
- Signature verification requires the signing secret (HS256) or public key (RS256, ES256)
Verification belongs in your auth library (jsonwebtoken for Node.js, pyjwt for Python, jjwt for Java). It's a 3-line operation in any language. Never paste your secret into a web tool to do it.
Common Claims Worth Checking
When debugging, these are the claims you'll inspect most:
subsubject (typically the user ID)iatissued at (Unix timestamp)expexpiration (Unix timestamp)ississuer (who created the token)audaudience (who the token is for)nbfnot before (token isn't valid before this time)
The JWT decoder automatically calculates expiry status from exp and shows whether the token is still valid.
Quick Reference
| Situation | Use this |
|---|---|
| One-off decode | Browser JWT decoder |
| Token already in DevTools | DevTools console one-liner |
| Scripting or CI | cut + base64 + jq pipeline |
| Verifying signature | Your server-side auth library |
For everyday JWT decoding, use a browser-based decoder that processes everything client-side. For verification, use your own code never a web tool.