DEV Community

RyanCadev
RyanCadev

Posted on

JWT decoder tools: what's safe, what's sketchy, and what I actually use

You're probably fine. But let me explain why.

Every few months a thread pops up on Reddit or Slack: "Is it safe to paste my JWT into jwt.io?"

The honest answer is: it depends on the token, and most devs already know the safe answer but want confirmation.

Here's the thing about JWTs — they're not encrypted by default. They're just base64-encoded. Decoding the header and payload reveals the claims (user ID, roles, expiry, etc.) but not the signature secret. So pasting the payload into a decoder doesn't inherently expose anything a motivated attacker couldn't already get from intercepting the token in transit.

But there are two real concerns worth thinking about:

  1. Access tokens with sensitive claims — Some JWTs contain internal user IDs, email addresses, org IDs, or permission scopes. Pasting those into a third-party site means you've sent that data to someone else's server.

  2. Trust surface — Even if jwt.io says it decodes client-side, do you know that for sure? Do you trust every CDN it loads from? Do you trust your browser extensions not to capture the input?

Most of the time, for a random dev token in a local dev environment: you're fine. But if you're debugging a production token with real user data? It's worth being a little more intentional.


Option 1: Decode it with atob() yourself

You don't need a tool at all. Open DevTools, paste this:

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

const [header, payload] = token.split('.').slice(0, 2).map(
  part => JSON.parse(atob(part.replace(/-/g, '+').replace(/_/g, '/')))
);

console.log('Header:', header);
console.log('Payload:', payload);
Enter fullscreen mode Exit fullscreen mode

Done. No network request. No third-party site. Zero trust required.

This is genuinely the cleanest option for one-off decoding when you're already in the browser or Node.


Option 2: Use a client-side decoder tool

Sometimes you want a nicer UI — syntax highlighting, expiry countdown, a clean view of all claims. For that, AnyTools.io has a JWT decoder that:

  • Runs entirely in your browser (no token sent to any server)
  • Requires no login, no account, no cookies
  • Shows header, payload, and expiry at a glance

I use it when I want something human-readable during a debugging session. It's part of a broader toolkit (JSON formatter, cron builder, color palette, etc.) that I keep bookmarked for the kind of small-but-annoying tasks that eat five minutes if you don't have the right tool.

(Full disclosure: I work as a marketing advocate for AnyTools.io. But I also actually use it, which is why I took the job.)


When to be more careful

If you're dealing with long-lived tokens, internal admin JWTs, or tokens that contain sensitive PII — consider decoding them on an air-gapped machine or in a private VM. The browser DevTools approach (atob()) is safest for those cases.

For everything else in a normal dev workflow, be intentional, not paranoid.


Quick reference

Scenario Best approach
Quick debugging, dev environment atob() in DevTools
Want a nice UI, no sensitive data Client-side decoder tool (e.g. anytools.io)
Production tokens with real PII DevTools only, or dedicated local tool
Batch decoding / automation Write a small script (jwt.decode() in Node)

TL;DR

JWT decoders don't need your secret key to read the payload — that part is just base64. The real risk is sending sensitive claim data to a third party's server. Mitigate that by using atob() in DevTools or a client-side-only tool. Don't panic, just be intentional.


If you've got a smarter approach or a tool I'm missing, drop it in the comments.

Top comments (1)

Collapse
 
ryancadev profile image
RyanCadev

Quick note — I'm affiliated with AnyTools.io, so I'm biased on that mention. The atob() approach in DevTools is genuinely the zero-trust option and I'd use it regardless.