What Is Navigator Fingerprinting? β Definition and How It Works
Learn which navigator properties are read for fingerprinting, why consistency matters more than values, and the classic tells.
Navigator fingerprinting reads the browser's navigator object β the self-description a browser offers about itself β and uses the combination as an identifying signature.
1. What Gets Read
| Property | Reveals |
|---|---|
userAgent |
Browser, version, platform claim |
platform |
OS architecture claim |
language / languages |
Locale preferences |
hardwareConcurrency |
Logical CPU count |
deviceMemory |
Approximate RAM tier |
maxTouchPoints |
Touch capability |
plugins / mimeTypes |
Installed plugin inventory |
webdriver |
Whether automation is declared |
vendor |
Engine vendor string |
userAgentData |
Structured client hints |
2. navigator.webdriver Is the First Thing Checked
The specification requires browsers under automation to set navigator.webdriver to true. It is one property, trivially read, and it is a declaration that the session is automated.
navigator.webdriver // true under plain WebDriver automation
Any detection worth the name checks this before anything else. It is why automation frameworks and their stealth plugins spend effort on it, and why a setup that leaves it untouched is identified instantly.
3. Consistency Beats Plausibility
The subtle and more important point: fingerprinting rarely looks for a "bad" value. It looks for combinations that cannot coexist.
Real contradictions that get caught:
userAgentclaiming Windows whileplatformreportsLinux x86_64.- A macOS user agent with
maxTouchPointsof 5. - A mobile user agent with a 2560-pixel screen and no touch support.
languageslisting onlyen-USwhile the timezone isEurope/Madrid.hardwareConcurrencyof 1 on a claimed modern desktop.- An empty
pluginsarray on a browser build that ships with some.
Each individual value is unremarkable. The combination is impossible, and impossibility is a far stronger signal than unusualness.
4. Why Spoofing One Property Backfires
Overriding userAgent alone creates precisely the contradictions above. The claimed browser and the actual engine behaviour then disagree β and engine behaviour is much harder to fake than a string.
// A UA claiming Safari, on a Chromium engine, is detectable from behaviour
'chrome' in window // Chromium tell
CSS.supports('-webkit-touch-callout', 'none')
navigator.userAgentData?.brands // Chromium-only API
The practical rule: change the whole profile coherently, or change nothing. A partially spoofed navigator is more identifiable than an honest one.
5. Client Hints Complicate It Further
navigator.userAgentData exposes structured, high-entropy hints β platform version, architecture, full browser version β separately from the user agent string.
That gives detection two independent sources for the same facts. Spoofing the string while leaving the hints untouched is an immediate mismatch, and it is one of the more common defects in stealth configurations.
6. Entropy and Uniqueness
Individually these properties are low-entropy: millions share a user agent. Combined β user agent, language list, CPU count, memory tier, touch points, plugin inventory β the set becomes narrow enough to be near-unique for many devices.
Add screen, timezone, and rendering fingerprints and identification becomes reliable without any cookie.
7. Practical Implications
- Do not override single properties. Use a coherent, complete profile.
- Keep headers and JavaScript consistent. The
User-Agentheader andnavigator.userAgentmust agree, as mustAccept-Languageandnavigator.languages. - Match the environment to the claim. A claimed macOS profile should not report a Linux platform.
- Match plausible hardware. Absurd CPU or memory values are conspicuous.
- Prefer a real browser in a real configuration over an elaborately patched one β fewer contradictions exist to find.
8. Summary
Navigator fingerprinting reads the browser's self-description β user agent, platform, languages, hardware hints, plugins, and webdriver β and identifies the combination.
navigator.webdriver is checked first because it is a declaration of automation, and the strongest signals are internal contradictions rather than unusual values β so spoof the entire profile coherently across headers, JavaScript properties, and client hints, or leave it alone.