The CreepJS proxy test does not look at your network at all. Its page, abrahamjuliot.github.io/creepjs/tests/proxy.html, hunts for JavaScript Proxy objects: the language feature that lets a script put a wrapper around a function and intercept every call to it. A flag there says something rewrote the browser's own functions from JavaScript. It says nothing about your IP address, your proxy seller or your VPN.

The name is the whole confusion. "Proxy" means two unrelated things here, and someone who has just pointed a profile at a residential exit reads the word one way when CreepJS meant the other.

Why a spoofing script uses Proxy

Most fingerprint spoofing that runs in the page has the same problem. It wants getParameter or toDataURL or navigator.hardwareConcurrency to answer differently, and it wants the replacement to look like the original. A plain function swapped in for a native one is easy to spot: ask it for its source with Function.prototype.toString and it prints the script's code instead of function getParameter() { [native code] }.

A Proxy around the real native fixes most of that tell. V8 renders a Proxy as native code, though without the function's name, so with a little extra work to put the name back the obvious check passes. That is why the approach is common, and why we used it ourselves. Before Mosaic had its own engine, every patch our injected script installed was a Proxy over the real native, chosen for exactly that reason.

Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.

How it works: Machines that exist

What the CreepJS proxy page checks instead

The page exists to find that construction anyway, by looking where the wrapper cannot hide. Our detection catalogue describes the row as probing for Proxy-wrapped natives directly:

  • The exception a trap throws. Push a native function into an edge case, a bad receiver or a strange argument, and the real one fails in one particular way. A wrapper that forwards the call often fails in a slightly different one, with a different error type or message.
  • How the handler shows up in a stack trace. When an error passes through a Proxy, the trace can carry a frame for the handler that the native call would never have.
  • Whether toString and the descriptor agree through a second realm. An iframe has its own copy of every built-in. Reach the wrapped function from there, ask the other realm's toString about it and read its property descriptor, and a wrapper that was careful in one realm is often careless in the other.

Each of those is a question with one right answer in a stock Chrome. A Proxy that answers any of them differently has named itself, whatever values it was set up to return.

Reading a flag correctly

If the page flags something in your browser, look for whatever runs JavaScript in your pages: a privacy extension that randomises canvas or WebGL, a "fingerprint protection" add-on, an automation framework's stealth plugin, or an anti-detect browser that spoofs from injected script. Changing your proxy will not move the result, because the page never asked about it.

Our catalogue's pass condition for the row says the same thing more bluntly: a flag is not a score to argue with. It points at one specific way of building a patch, and in a script that builds every patch the same way, one finding applies to all of them.

The neighbouring pages make a set. CreepJS's prototype page catches the other half, a patch installed as a plain function instead of a Proxy, by walking every DOM prototype for descriptors, arity and names that differ from the real browser. Between the two there is no comfortable way to spoof from JavaScript: a plain function fails one page and a Proxy is what this one is looking for. What a CreepJS lie is covers how the main page counts these.

How a network proxy does get noticed

The worry behind the search is usually real, just aimed at the wrong page. Sites notice a network proxy by comparison. The IP's location against the browser's timezone and language is the first one any consistency scanner makes, and it costs them nothing. The owner of the address against what a home connection looks like is the second. WebRTC sending traffic around the proxy, and so naming a different address, is the third.

Those are the things a profile has to get right, and none of them involve a JavaScript Proxy. In Mosaic the timezone, language and geolocation follow the proxy's measured exit, and with a proxy attached the engine stops WebRTC from going around it.

Why the page has nothing to find on our engine

We keep the row in our catalogue because it is the closest thing to an adversarial review of the old injection's core decision. On Tesseract Chrome, Mosaic's own Chromium, fingerprint values are changed in C++ inside the engine, so every page, frame and worker sees one machine and there is no wrapper in the page at all. We also do not layer a JavaScript spoof on top of it, because two spoofs of one value compose into something neither predicted, and a scanner reads the result. How that engine draws its values from real hardware is the other half of the story.