DEV Community

sharefun2023
sharefun2023

Posted on Originally published at 23232322.xyz

I Counted How Many Free Dev Tools Quietly Upload Your Data. Then I Built 80 That Don't.

Paste a JWT into a "free online JWT debugger." Paste a config file into a "free JSON formatter."

Then open DevTools, hit the Network tab, and watch it POST your payload to api.something-saas.com/v1/parse.

I got annoyed enough to check. Of the first dozen "free" tools I tried, eight shipped data off the device. Some had a privacy policy. Some had a privacy policy that said "we may share data with partners." Your production tokens, in a partner's S3 bucket.

Most of them weren't even doing it to be evil. They just built a Next.js app, needed the logic somewhere, and the server was already there. Formatter on the server. Base64 decode on the server. For a 3-line JSON.stringify.

The part that actually costs you

It's not just privacy theater. Server-side tooling has real costs you pay every day:

  • Latency. Round-trip to a region that isn't yours, on a connection that isn't yours. My regex tester took ~400ms to say "no match."
  • Rate limits and paywalls. Free tier hits you at 10 operations. The tool that was "free" is now a $9/mo subscription for btoa.
  • Availability. Their server goes down, your tool goes down. Great during an incident, when you actually need to decode something.
  • Offline. On a plane, on a locked-down corporate network, the tool is gone.

Every one of those problems disappears if the code runs in the browser tab you already have open.

What client-side actually means

There is no API. There is no fetch to a backend. Network stays empty except for the static assets. Concretely:

// Base64, no server. 2 lines and no data leaves the tab.
const encode = (s) => btoa(String.fromCharCode(...new TextEncoder().encode(s)));
const decode = (s) => new TextDecoder().decode(Uint8Array.from(atob(s), c => c.charCodeAt(0)));
Enter fullscreen mode Exit fullscreen mode
// JWT verify with a real signature check, in-page.
// WebCrypto handles HS256/384/512 and RS/ES via importKey.
const [h, p, s] = jwt.split(".");
const key = await crypto.subtle.importKey(
  "raw", new TextEncoder().encode(secret),
  { name: "HMAC", hash: "SHA-256" }, false, ["verify"]
);
const sig = Uint8Array.from(
  atob(s.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)
);
const ok = await crypto.subtle.verify(
  "HMAC", key, sig, new TextEncoder().encode(`${h}.${p}`)
);
Enter fullscreen mode Exit fullscreen mode

That's the whole "backend." The <input> is the API.

The interesting cases are where people assume you need a server:

Task Why people think it needs a server How it's actually done client-side
SQL formatting "Parsing is hard" Tokenizer + formatter in a Web Worker — keeps the main thread at 60fps
Image conversion "Encoding is server work" Canvas + toBlob('image/webp')
SVG to PNG "Rasterization needs a service" Draw the SVG into a canvas via a data-URI image
Regex testing "Sandboxing" It's a regex. RegExp has been in the engine forever
Hashing (SHA/MD5) "Performance" crypto.subtle, native speed

WebCrypto also gives you something the SaaS tools don't: the key never crosses the wire. A server-side JWT verifier has to receive your signing secret to check a signature. That's a much bigger deal than the payload leak.

What I built

23232322.xyz — 80+ tools, one page each, no build step. JSON formatter and validator, Base64 encode/decode, regex tester, JWT debugger with real HS256/384/512 and RS/ES256-512 verification, hash generators, UUID, URL encode/decode, diff checker, HTML minifier, and a bunch more.

No account. No upload. No analytics on your input. Source for each page is plain HTML + vanilla JS, so you can read it and confirm nothing is being sent anywhere.

How to check any tool yourself in 20 seconds

Before you trust any "free online X":

  1. Open DevTools → Network.
  2. Filter to Fetch/XHR.
  3. Use the tool with obvious dummy data ({"test":1}).
  4. If a request appears with your input in the body — that data left your machine.

Do that to my tools too. It's the point.

If a tool can't be written this way, it's because it needs a real backend. Most of them don't. They just needed somewhere to put the code.

The whole set is at 23232322.xyz.

Top comments (0)