Browser Fingerprinting: Everything That Gets Detected
A complete catalog of browser fingerprinting signals used by anti-bot systems — from canvas and WebGL to AudioContext, fonts, and navigator properties. What each signal reveals and why it matters for automation.
Every time your browser connects to a website, it leaks dozens of signals that combine into a unique fingerprint. Anti-bot systems like Cloudflare, DataDome, and Imperva collect these signals to distinguish real users from automation.
This is a complete reference of what gets fingerprinted, how each signal works, and what makes automation tools detectable.
What Is Browser Fingerprinting?
Browser fingerprinting identifies a browser session by combining multiple signals into a unique hash. Unlike cookies, fingerprints can’t be deleted — they’re derived from your browser’s configuration, hardware, and behavior.
A single signal isn’t unique. Millions of people use Chrome on Windows. But combine Chrome + Windows + specific GPU + screen resolution + installed fonts + timezone + language — and the combination narrows to a handful of users. Or one.
Anti-bot systems don’t need a 100% unique fingerprint. They need enough signals to answer one question: does this look like a real browser on real hardware, or automation on a server?
The Fingerprint Taxonomy
Signals fall into six categories:
- Navigator API — What the browser says about itself
- Rendering — How the browser draws pixels
- Hardware — What machine it’s running on
- Network — Connection-level signals
- Behavior — How the user interacts
- JavaScript environment — Runtime quirks and inconsistencies
Let’s go through each one.
1. Navigator API Signals
The navigator object is the first thing anti-bot scripts check. It’s the browser’s self-description.
navigator.webdriver
The most obvious signal. Automation frameworks set this to true:
- Selenium →
true(via ChromeDriver) - Puppeteer →
true(unless patched) - Playwright →
true(unless patched) - Real browser →
undefinedorfalse
Stealth plugins override this to false, but anti-bot systems check the property descriptor too. A patched webdriver has a different Object.getOwnPropertyDescriptor() result than a native one.
navigator.plugins
Real desktop browsers have plugins (PDF viewer, Chrome PDF Viewer, etc.). Headless Chrome historically returned an empty PluginArray.
Modern stealth patches fake the plugins list, but the faked objects often fail deep inspection — checking plugin[0].constructor.name or iterating with Symbol.iterator can reveal fakes.
navigator.languages
Must be consistent with:
Accept-LanguageHTTP headerIntl.DateTimeFormat().resolvedOptions().locale- Timezone setting
A browser claiming en-US but with timezone Asia/Tokyo is suspicious.
navigator.hardwareConcurrency
Returns CPU core count. Servers typically report 1-2 cores (or a high number like 96). Real laptops report 4-16. A mismatch between reported cores and other hardware signals is a flag.
navigator.deviceMemory
RAM in GB (rounded). Servers may report unusual values. Combined with hardwareConcurrency, this creates a hardware profile that should be consistent.
navigator.platform
Should match the User-Agent. A UA claiming “Windows NT 10.0” but navigator.platform returning “Linux x86_64” is an instant flag.
navigator.maxTouchPoints
Desktop browsers return 0. Mobile browsers return a positive number. If your User-Agent claims to be an iPhone but maxTouchPoints is 0, you’re caught.
2. Rendering Fingerprints
These are the hardest signals to fake because they depend on actual hardware rendering.
Canvas Fingerprinting
The browser draws a complex scene on an invisible <canvas> element — text with specific fonts, gradients, emojis, mathematical curves. Then the pixel data is extracted and hashed.
Why it works: different GPUs, font rendering engines, and anti-aliasing implementations produce slightly different pixel outputs. The same code produces a different hash on:
- Chrome on Windows (DirectWrite rendering)
- Chrome on macOS (Core Text rendering)
- Chrome on Linux (FreeType rendering)
- Headless Chrome (software renderer → distinct hash)
Why headless fails: Headless Chrome uses a software renderer (Swiftshader) instead of a real GPU. The canvas output is detectably different from any real hardware.
WebGL Fingerprinting
WebGL exposes the GPU directly. Anti-bot scripts collect:
- Renderer string — e.g., “ANGLE (NVIDIA GeForce RTX 3080, D3D11)” This identifies the exact GPU. Headless Chrome reports “Google SwiftShader” — a dead giveaway.
- Vendor string — “Google Inc.” for SwiftShader vs real GPU vendors
- Supported extensions — The list and order of WebGL extensions varies by GPU/driver
- Shader precision —
getShaderPrecisionFormat()returns hardware-specific values - Max parameters —
MAX_TEXTURE_SIZE,MAX_VERTEX_ATTRIBS, etc. differ by GPU
WebGL2 and WebGPU
Newer APIs expose even more hardware details. WebGPU’s adapter.requestAdapterInfo() gives direct access to GPU architecture information.
AudioContext Fingerprinting
The browser processes audio through the Web Audio API. A simple oscillator → compressor → analyser chain produces output that varies by:
- Audio hardware/drivers
- OS audio stack
- Browser implementation
- Sample rate support
The output is a float array that gets hashed. Like canvas, the software audio stack in headless environments produces distinct output.
3. Hardware Signals
Screen Properties
screen.width/screen.height— Real displays have standard resolutions. Headless browsers often use default 800×600 or whatever was configured.screen.colorDepth— Usually 24 or 30 on real displays.window.devicePixelRatio— Retina displays report 2 or 3. Headless defaults to 1.screen.availWidthvsscreen.width— The difference reveals the OS taskbar size. A zero difference suggests no real display.
Font Enumeration
Browsers don’t expose an installed fonts API, but anti-bot scripts detect fonts indirectly:
- Render text in a
<span>with a fallback font - Measure the
offsetWidthandoffsetHeight - Compare against the same text with a test font
- If dimensions differ, the test font exists
This reveals the OS (Windows has Segoe UI, macOS has San Francisco, Linux has Liberation) and catches headless environments that have minimal font sets.
Battery API
navigator.getBattery() reveals:
charging— Servers are always “charging”chargingTime— Real laptops have finite valueslevel— Always 1.0 on servers
Most browsers have deprecated or restricted this API, but some anti-bot systems still check.
4. Network Signals
TLS Fingerprinting (JA3/JA4)
Before any HTTP traffic, the TLS handshake reveals:
- Cipher suite order
- Extension order
- Supported TLS versions
- Elliptic curves
Each browser/version has a characteristic TLS fingerprint. HTTP libraries (Python requests, Node axios) have completely different fingerprints from real browsers.
We covered this in depth in How Cloudflare Bot Detection Works.
HTTP/2 Fingerprinting (Akamai)
HTTP/2 settings frames contain:
SETTINGS_HEADER_TABLE_SIZESETTINGS_INITIAL_WINDOW_SIZESETTINGS_MAX_CONCURRENT_STREAMS- Priority frame ordering
These vary by browser. Akamai’s bot detection specifically uses HTTP/2 fingerprinting.
Header Order
HTTP headers arrive in a specific order. Chrome, Firefox, and Safari each send headers in a characteristic sequence. Automation tools often send headers in a different order than the browser they’re impersonating.
5. Behavioral Signals
Mouse Movement
Real humans move the mouse in curves with acceleration and deceleration. Bots either:
- Don’t move the mouse at all (no
mousemoveevents) - Move in perfectly straight lines
- Move with uniform speed (no acceleration)
- Teleport between points (instant position changes)
Anti-bot systems collect mouse event timestamps and positions, then analyze the movement pattern.
Keyboard Events
Real typing has:
- Variable inter-key delays (50-300ms)
- Occasional backspaces
- Different patterns for different key combinations
keydown→keypress→keyupevent sequence
element.value = "text" or inputEvent dispatched programmatically doesn’t produce the full event sequence.
Scroll Behavior
Humans scroll with inertia — momentum scrolling that decelerates. Trackpads produce smooth wheel events with fractional deltaY values. Programmatic scrolling (window.scrollTo) produces no wheel events.
Timing Patterns
- Time between page load and first interaction
- Time between clicks
- Reading time per page (dwell time)
- Request cadence across pages
Uniform timing across any of these is a strong bot signal.
6. JavaScript Environment
Automation-Specific Properties
Anti-bot scripts check for the existence of:
window.chrome.runtime— Missing in headless Chromewindow.__nightmare— Nightmare.jswindow._phantom— PhantomJSwindow.callPhantom— PhantomJSwindow.domAutomation— Chrome automationdocument.__selenium_unwrapped— Seleniumdocument.__webdriver_evaluate— Old WebDriverdocument.$cdc_asdjflasutopfhvcZLmcfl_— ChromeDriver
Prototype Tampering Detection
When stealth scripts override properties like navigator.webdriver, anti-bot systems detect it by:
// Check if the property is truly native
const desc = Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver');
// Native: configurable: true, enumerable: true, get: function, set: undefined
// Patched: often has wrong descriptor shape
iframe Isolation Tests
Anti-bot scripts create a fresh <iframe>, get a reference to its navigator, and compare against the main window’s navigator. Stealth patches applied to the main window don’t automatically propagate to new iframes.
toString() Checks
Native browser functions have a specific toString() output:
navigator.webdriver.toString() // "[object Navigator]"
// vs patched versions that might return different output
Overridden getters can be detected by checking if Function.prototype.toString.call(getter) returns "function get webdriver() { [native code] }".
The Consistency Problem
Individual signals can be faked. The real challenge is consistency across all signals simultaneously.
Your fingerprint must tell a coherent story:
- Canvas output must match the GPU claimed by WebGL
- Font list must match the OS claimed by User-Agent
- TLS fingerprint must match the browser version
- Screen resolution must be plausible for the device
- Audio fingerprint must match the hardware profile
- Timezone must match the IP geolocation
One inconsistency is enough to flag the session. This is why “stealth plugins” that patch individual checks keep falling behind — there are too many signals to fake independently.
How hidettp Handles This
Instead of patching individual signals on top of headless Chrome, hidettp runs real browser profiles on real hardware. Every signal is genuine because it comes from an actual browser environment — not a simulation.
The canvas output matches the GPU. The fonts match the OS. The TLS fingerprint matches the browser version. There’s nothing to detect because there’s nothing fake.
Building automation that needs to pass fingerprint checks? Join the hidettp waitlist →
Further Reading
- How Cloudflare Bot Detection Works — Our deep dive on Cloudflare’s full detection stack
- CreepJS — Open-source fingerprint inspector (test your own browser)
- FingerprintJS — Commercial fingerprinting library docs
- Brotector — Open-source bot detection test
hidettp is in private beta.
Get early access, founding-member pricing, and a direct line to the team.
JOIN WAITLIST