Back to blog
·5 min read

How Detection Systems Catch Browser Fingerprint Spoofing

Changing your User-Agent string takes one line of code. Actually passing as a different browser takes hundreds of consistent signals across dozens of APIs. Here's how detection systems catch the gaps.

The consistency problem

Every browser exposes a constellation of signals that are internally consistent. A specific version of Chrome on Windows 11 with a given GPU has a specific set of JavaScript APIs, CSS features, WebGL parameters, fonts, plugin lists, and permission defaults.

Spoofing means faking all of these simultaneously, and they all need to be consistent with each other. Miss one, and you've created a fingerprint that doesn't match any real browser, which is more suspicious than not spoofing at all.

Detection technique 1: Version feature mismatch

Every Chrome version adds and removes JavaScript APIs, CSS features, and Web Platform capabilities. Detection systems maintain databases of which features exist in which versions.

If you claim a Chrome version via User-Agent but your browser supports features only available in a newer version, or is missing features that should be present, you're caught. For example, CSS.highlights was added in Chrome 105. A browser claiming to be Chrome 103 but supporting CSS.highlights has an inconsistent fingerprint.

// Simplified detection logic
const claimedVersion = parseVersionFromUA(navigator.userAgent)
const hasHighlightsAPI = 'highlights' in CSS

if (claimedVersion < 105 && hasHighlightsAPI) {
    // Feature exists in a newer version than claimed, likely spoofed
}

StealthCheck uses a database of browser features (CSS, JS, window globals) per version to run these checks. A single mismatch triggers a version consistency issue.

Detection technique 2: Prototype chain validation

When stealth plugins patch navigator.plugins, they typically create a plain JavaScript array and assign it. But the real navigator.plugins is a PluginArray with a specific prototype chain:

// Real browser
navigator.plugins instanceof PluginArray // true
Object.getPrototypeOf(navigator.plugins) === PluginArray.prototype // true
navigator.plugins.toString() // "[object PluginArray]"

// Spoofed (naive patch)
navigator.plugins instanceof PluginArray // false, caught

The same applies to navigator.mimeTypes (MimeTypeArray), individual Plugin objects, and many other DOM interfaces. Detection scripts walk the prototype chain and verify each level.

Detection technique 3: toString integrity

Native browser functions have a specific toString() output:

// Real browser
navigator.sendBeacon.toString()
// "function sendBeacon() { [native code] }"

// Monkey-patched
navigator.sendBeacon.toString()
// "function () { return true }"

Stealth plugins often patch Function.prototype.toString to return [native code] for their fakes. But detection scripts can detect this. For example, they create a fresh iframe and compare toString behavior between the main frame and the iframe's unmodified environment.

Detection technique 4: Worker scope analysis

Web Workers run in their own global scope with their own navigator object. Many stealth plugins only patch the main frame and don't touch workers at all.

// Detection via Worker
const blob = new Blob(
    [`postMessage(navigator.webdriver)`],
    { type: 'text/javascript' },
)
const worker = new Worker(URL.createObjectURL(blob))
worker.onmessage = (e) => {
    if (e.data === true) {
        // Main frame was patched, but worker wasn't
    }
}

StealthCheck collects signals from dedicated, shared, and service worker scopes. It checks navigator properties, error stack formats, toString behavior, math precision, timezone, and more, then compares them against the main frame.

Detection technique 5: Cross-signal consistency

Even if every individual signal passes, detection systems look for combinations that don't exist in the real world:

Signal Value Problem
User-Agent Chrome / macOS OK
Fonts Segoe UI, Calibri Windows-only fonts on "macOS", caught
Screen 1920x1080 OK
deviceMemory 0.25 0.25 GB: no modern device has 256 MB of RAM
Platform MacIntel OK
Timezone Asia/Tokyo Possible, but combined with en-US locale? Suspicious

These cross-signal checks are why spoofing is fundamentally harder than changing a few values. You're maintaining a consistent identity across every API the browser exposes.

Detection technique 6: Canvas and WebGL hashing

Canvas fingerprinting renders specific shapes and text, then hashes the pixel output. Every browser+OS+GPU combination produces a slightly different result due to differences in rendering engines, font rasterizers, and GPU drivers.

Detection systems can maintain databases of known canvas hashes:

  • Known legitimate: hashes produced by real browser/OS/GPU combinations
  • Known spoofed: hashes characteristic of popular spoofing tools
  • Unknown: hashes that match nothing in the database

An unknown hash isn't conclusive on its own (minor software differences can produce unique hashes), but combined with other signals it adds weight.

What effective anti-detection looks like

The most successful approaches don't try to fake signals. They produce authentic ones:

  1. Real browser binaries. Not patched Chromium builds, but the actual Chrome binary.
  2. Real user data directories. With authentic cookies, extensions, and preferences.
  3. Consistent hardware profiles. If you claim a specific GPU, all WebGL parameters must match.
  4. Matching timezone/locale/IP geolocation. These need to tell the same geographic story.
  5. Proper browser chrome. Realistic window dimensions with toolbar offset.

Verifying your fingerprint

Whether you're testing anti-detect browsers, stealth plugins, or custom patches, you can verify against the same types of checks described above using StealthCheck's free test. It runs 190+ detection checks across 9 scoring categories and tells you exactly which signals are inconsistent.

Further reading

Test your browser stealth for free

Run a free fingerprint test and see exactly what detection systems see.