All posts
browser-fingerprintinganti-botweb-scrapingprivacy

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.

h
hidettp team

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:

  1. Navigator API — What the browser says about itself
  2. Rendering — How the browser draws pixels
  3. Hardware — What machine it’s running on
  4. Network — Connection-level signals
  5. Behavior — How the user interacts
  6. 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.

The most obvious signal. Automation frameworks set this to true:

  • Seleniumtrue (via ChromeDriver)
  • Puppeteertrue (unless patched)
  • Playwrighttrue (unless patched)
  • Real browserundefined or false

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.

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.

Must be consistent with:

  • Accept-Language HTTP header
  • Intl.DateTimeFormat().resolvedOptions().locale
  • Timezone setting

A browser claiming en-US but with timezone Asia/Tokyo is suspicious.

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.

RAM in GB (rounded). Servers may report unusual values. Combined with hardwareConcurrency, this creates a hardware profile that should be consistent.

Should match the User-Agent. A UA claiming “Windows NT 10.0” but navigator.platform returning “Linux x86_64” is an instant flag.

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 precisiongetShaderPrecisionFormat() returns hardware-specific values
  • Max parametersMAX_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.availWidth vs screen.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:

  1. Render text in a <span> with a fallback font
  2. Measure the offsetWidth and offsetHeight
  3. Compare against the same text with a test font
  4. 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 values
  • level — 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_SIZE
  • SETTINGS_INITIAL_WINDOW_SIZE
  • SETTINGS_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 mousemove events)
  • 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
  • keydownkeypresskeyup event 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 Chrome
  • window.__nightmare — Nightmare.js
  • window._phantom — PhantomJS
  • window.callPhantom — PhantomJS
  • window.domAutomation — Chrome automation
  • document.__selenium_unwrapped — Selenium
  • document.__webdriver_evaluate — Old WebDriver
  • document.$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

Ready to automate the protected web?

hidettp is in private beta.

Get early access, founding-member pricing, and a direct line to the team.

JOIN WAITLIST
Back to all posts RSS Feed