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:
- Real browser binaries. Not patched Chromium builds, but the actual Chrome binary.
- Real user data directories. With authentic cookies, extensions, and preferences.
- Consistent hardware profiles. If you claim a specific GPU, all WebGL parameters must match.
- Matching timezone/locale/IP geolocation. These need to tell the same geographic story.
- 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.