The JavaScript Runtime Quirks That Expose Anti-Detect Browser Spoofing
I run proxy infrastructure and cloud phones for a living, and multi-accounting is part of that world whether anyone likes it or not. Every anti-detect browser I’ve tested, from the well-known names down to obscure forks, does the same basic thing: it patches the JavaScript runtime so the page sees a different navigator, screen, and hardware profile than the machine actually has. The problem is that patching a runtime and replacing it are two different jobs. Most tools do the first one. Detection scripts are built almost entirely around finding the seams left by the second.
This is not a guide to beating any platform’s detection or terms of service. It’s an explanation of the mechanics, because understanding how JavaScript runtime detection works is the only way to judge whether a given browser’s spoofing is thorough or cosmetic.
Why the runtime itself is the battleground
A browser fingerprint isn’t one signal, it’s the sum of dozens of JavaScript properties and API responses: navigator.userAgent, screen.width, WebGL renderer strings, Intl.DateTimeFormat timezone data, font metrics, audio processing output, and more. Anti-detect browsers override these values so a script reading navigator.platform gets “Win32” instead of whatever the host OS actually is. That part is straightforward and every serious tool does it.
The harder part is that JavaScript engines expose metadata about functions and objects themselves, not just their return values. Detection scripts don’t just read navigator.hardwareConcurrency, they inspect how that property is implemented. If the implementation looks different from a stock browser, that’s a signal on its own, independent of whether the spoofed value is plausible.
The [native code] tell
Every built-in browser function, when you call .toString() on it, returns something like function get hardwareConcurrency() { [native code] }. That’s how V8 represents compiled native functions. When a script overrides navigator.hardwareConcurrency with a JavaScript getter (which is how most spoofing works), the naive version of that override returns the actual JavaScript source of the getter function, not [native code].
Detection scripts have been calling Function.prototype.toString on suspicious properties for years now. A tool that overrides a property but forgets to also patch toString() on that override leaves an obvious trace: a native-sounding property whose source code is visible. The fix, patching toString itself, is well known among anti-detect vendors at this point, but it has to be applied consistently across every single overridden property, and that consistency is exactly where cheaper implementations fall short.
Proxy traps leave fingerprints of their own
A lot of modern spoofing uses JavaScript Proxy objects to intercept property access dynamically rather than hardcoding values with Object.defineProperty. Proxies are flexible, but they change how an object behaves under introspection. Calling Object.getOwnPropertyDescriptor on a proxied property, or running Object.keys() or JSON.stringify() against a proxied navigator object, can return a shape that differs subtly from what a real, unmodified navigator object returns; things like configurable and enumerable flags on the descriptor, or the exact prototype chain reported by Object.getPrototypeOf.
None of this is visible to a human looking at the page. It’s only visible to a script written specifically to probe object internals rather than just read surface values. That’s the core of why “the page looks right in the browser” tells you very little about whether the runtime underneath is well spoofed.
Timing side channels nobody thinks about
Spoofing a timezone means overriding Intl.DateTimeFormat().resolvedOptions().timeZone and Date.prototype.getTimezoneOffset(). But performance.now() and requestAnimationFrame timing are driven by the actual host clock and actual GPU compositor, not by anything JavaScript-overridable in the same way. A detection script can compare the claimed timezone against subtle timing behavior, like how frame timing drifts relative to Date.now(), or check whether performance.timeOrigin lines up with other clock-derived values on the page. These are second-order checks, not something every site runs, but they exist precisely because the first-order overrides (timezone strings, locale strings) are the easiest thing for any anti-detect browser to get right, so serious detection systems have moved past checking them directly.
Client hints versus the user agent string
Modern Chromium sends User-Agent Client Hints, the Sec-CH-UA family of headers and the navigator.userAgentData API, alongside the traditional navigator.userAgent string. These are generated by the actual browser engine build, not just a string you can set. If an anti-detect browser patches navigator.userAgent to claim a different Chrome version or OS but doesn’t fully align navigator.userAgentData.getHighEntropyValues() and the outgoing Sec-CH-UA headers, a script that checks both will see two different browsers claiming to be the same session. This is one of the more common gaps I’ve seen when testing tools that were clearly built before client hints became standard and never fully retrofitted.
Automation flags nobody remembers to clear
A separate category of tell comes from browser automation itself, not from fingerprint spoofing. navigator.webdriver is set to true by default in automated Chromium sessions. Older automation stacks left behind window.cdc_* variables from ChromeDriver, or triggered detectable behavior when the Chrome DevTools Protocol’s Runtime.enable was called during setup. Anti-detect browsers that are built on top of a modified Chromium and driven through CDP-based automation have to specifically strip these markers. It’s a solved problem for the major vendors, but it’s also exactly the kind of thing that regresses silently after a Chromium version bump if a vendor isn’t rigorously testing every release against known detection scripts.
A related mismatch: navigator.permissions.query({name: 'notifications'}) and Notification.permission are supposed to agree with each other in a real browser. Some spoofing setups patch one and not the other, which is a small inconsistency but a checkable one.
Hardware claims that contradict each other
WebGL exposes a vendor and renderer string through WEBGL_debug_renderer_info, which normally reflects the actual GPU. navigator.deviceMemory and navigator.hardwareConcurrency are supposed to reflect real device specs, within the coarse buckets browsers already round them to. A profile that claims to be a mid-range phone but reports a desktop GPU renderer string, or claims 4GB of device memory alongside a 16-core hardwareConcurrency, is internally inconsistent in a way any script checking more than one property at once will catch. This is less about clever detection code and more about the profile itself being poorly built, which is common in cheaper or less-maintained tools.
What this actually means if you’re evaluating tools
None of this makes any anti-detect browser undetectable, and nothing here is a claim that a given tool will keep an account from getting banned. What it does tell you is where to look when you’re deciding whether a tool’s spoofing is serious: does it patch toString consistently across overridden properties, does it keep client hints aligned with the user agent string, does it keep hardware-related properties internally consistent with each other, and does it get retested against new Chromium releases rather than shipped once and left alone. Vendors like Multilogin, GoLogin, Kameleo, AdsPower, and Dolphin Anty all take different approaches to these layers, and the differences show up in exactly the checks described above, not in how the browser looks when you’re using it. Testing a profile against a public fingerprinting or bot-detection demo before you rely on it for anything that matters will tell you more than any vendor’s marketing page will.
If you want more breakdowns like this on how browser fingerprinting and anti-detect tooling actually hold up under testing, head back to the homepage.
Get new guides and videos first — join the Telegram channel.