Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

How to check whether a "client-side" web tool really stays client-side

"Processed in your browser." "Your files never leave your device." "100% client-side." Those sentences cost nothing to write. I've audited a dozen tools that say them, and the claim is true for maybe half. The failure m

"Processed in your browser." "Your files never leave your device." "100% client-side."

Those sentences cost nothing to write. I've audited a dozen tools that say them, and the claim is true for maybe half. The failure mode is rarely a lie — it's usually a half-truth: the preview is local, the export is uploaded; or the file is uploaded but "only for processing"; or the compression happens in the tab and then the original quietly goes to a CDN for "backup".

Here are the four checks I run, in the order that catches the most of it fastest. DevTools and about five minutes per tool.

1. Watch what leaves the tab

Open DevTools → Network, tick Disable cache, then run one real file through the tool.

  • Filter to Fetch/XHR and sort by Size, descending.
  • Look for any POST / PUT whose request body is roughly the size of your file (or the sum of several chunked requests matching it).
  • Also check the WS filter. An upload over a WebSocket won't show up as an XHR, and it's a common way to hide one.

A tool that processes locally will show you JS, a wasm binary, maybe fonts — and nothing carrying your file. A tool that uploads will show a request with a payload you can literally see in the Payload tab.

Two things that make this check lie to you: an upload that's split into 64 KB chunks looks small in every individual row (sum them), and an upload triggered only on download looks clean until you click export. So do the check twice — once on import, once on export.

2. Turn the network off

Not "throttle to offline" — actually turn Wi-Fi off, then reload.

  • Page still loads and the tool still works → the processing really is local, or at minimum the whole pipeline is cached.
  • Page loads from cache but the tool hangs on "processing" → the engine is remote. That's the single loudest signal you'll get.

One caveat: a PWA that cached its shell will load offline even if its engine is a server API. That's why check 2 never stands alone — it's a filter, not a verdict.

3. Count the third-party requests

This is where "local" starts needing a definition, and where most audits stop too early.

Open the Network panel on a cold load (hard reload, cache disabled) and look at the Domain column. What you'll typically find:

Request type Does it leak the file? Does it leak you?
Your page + JS bundle No IP, UA, Referer
wasm engine from a public CDN No IP, UA — and the CDN operator sees which tool you're using
Google Fonts / analytics No IP, UA, page, sometimes a client id
Ad script No IP, UA, plus whatever the ad network fingerprints
POST with your file in it Yes —

So: "your file never leaves your device" and "this page makes zero network requests" are different claims. A tool can honestly make the first and still phone home constantly. When a tool's privacy page conflates the two, that's not proof of bad faith, but it is a reason to run check 4.

4. Look for the machinery

If processing is local, something in the tab has to do the work. Local processing has a fingerprint:

  • a .wasm file (ffmpeg, sql.js, ImageMagick, OpenCV — anything heavy),
  • or a Web Worker (worker.js in the Network panel, or a blob: worker),
  • or hot canvas / OffscreenCanvas usage for image work.

If you find none of those and the only thing that happens when you hit "process" is a fetch('/api/...'), you're looking at a server. No wasm, no worker, no canvas, but a 2.4 MB → 210 KB result — that's arithmetic happening somewhere else.

The three fake-local patterns

These are the ones I keep meeting. All three can claim "client-side" without technically lying.

1. Local preview, remote export. The crop/trim UI is local (it's just CSS and a <video> element), the actual encode is an upload. Check: does the export button fire a request?

2. Local processing, remote copy. The file is compressed in the tab and also sent to storage "so you don't lose it". Check: is there a PUT to somewhere that isn't your own download?

3. Local engine, remote data. The wasm runs in the tab, but the file is passed to a third-party API for OCR / transcription / "AI enhance". Check: any POST after the wasm finishes.

The honest boundary

I'd rather state the limit of this method than oversell it.

Local processing does not mean zero network requests. The page, the JS, the wasm, the fonts, and any analytics or ad script all reveal your IP and user agent. What "local" should mean is narrower and testable: the bytes of your file are read by code running in your tab, and no request carries them anywhere. That's the claim worth checking, and all four checks above test exactly that.

If you want to verify a specific tool, the fastest route is usually its own documentation of third-party requests. A tool that lists them is telling you it expects to be checked; a tool that only says "we care about privacy" is telling you it doesn't.

Disclosure: I build one of these — a set of 117 browser tools at wtoolskit.com. Free, no account. Don't take my word for it: the four checks above are exactly how I'd audit it, and the network panel doesn't care who wrote the page.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.