Most developers paste production JWTs into online decoders without thinking. Here's a 10-second DevTools check to see if your token is actually leaving your machine.
A coworker was debugging an auth bug last month. Standard workflow: copy the JWT from the failing request, paste it into an online decoder, read the payload. I've done it a thousand times. You probably have too.
Except the token he pasted belonged to a real customer. And the decoder he used is owned by an identity company that's had its share of security incidents.
Nothing bad happened. Probably. But it made me think about something I'd never actually checked: when you paste a JWT into an online decoder, where does that token go?
What a JWT actually contains
Quick reminder of why this matters. A JWT isn't encrypted — it's just Base64URL-encoded. Anyone who has the token can read everything in it:
header.payload.signature
The payload routinely contains:
- User ID, email, and role
- Session identifiers
- Token expiry (
exp) and issue time (iat) - Sometimes — against best practice — far more
And here's the part people forget: a valid, unexpired JWT is a live credential. If it hasn't expired, whoever holds it can often impersonate the user. Pasting it into a random website is functionally similar to pasting a password.
The 10-second check
Most online JWT decoders claim to be "secure" and "client-side." Some are. Some aren't. You don't have to trust the claim — you can verify it yourself in 10 seconds:
- Open the decoder in your browser
- Open DevTools → Network tab
- Clear the network log
- Paste a JWT and decode it
- Watch the Network tab
If any request fires when you decode — your token left your machine. A truly client-side decoder fires zero network requests during decoding. The JavaScript does everything locally; nothing is sent anywhere.
Try this on whatever decoder you currently use. You might be surprised.
Why most "online" tools send data
It's usually not malicious. Building decoding logic on the server is sometimes just easier, or the tool wants analytics on what's being decoded, or there are ads loading third-party scripts onto the same page where your token is now sitting in the DOM. Even if the tool itself is trustworthy, every third-party script on that page can read what's on it.
For a throwaway test token, who cares. For a production token with a real user's session — that's the difference between a habit you should keep and one you should drop.
The fix: decode locally
Two good options:
1. Decode it yourself. It's genuinely trivial:
function decodeJWT(token) {
const [header, payload] = token.split('.');
return {
header: JSON.parse(atob(header)),
payload: JSON.parse(atob(payload)),
};
}
(Note: real JWTs use Base64URL, so for tokens with - or _ you'll want to convert those before atob. But this is the gist.)
2. Use a decoder that's verifiably browser-only. I ended up building a JWT decoder that runs entirely in the browser — no signup, no ads, no server. Run the DevTools check on it and you'll see zero requests fire when you decode. That was the whole point: I wanted a decoder I could paste a production token into without thinking twice.
The takeaway
Pasting tokens into online tools is muscle memory for most of us. That's fine — until the token is real.
Next time you reach for an online decoder, spend 10 seconds in the Network tab first. If the tool sends your token anywhere, find one that doesn't. Your future incident-response self will thank you.
What's your JWT debugging workflow? Do you decode locally or trust an online tool? Curious what others do.













