← All posts
How anti-scraping works · Part 1 of 10

Anatomy of a browser fingerprint

What browser fingerprinting is, the four signal layers anti-bot systems read (network, TLS, runtime, behavior), and how much each one reveals.

By the ScrapePass team···9 min read·Sources checked October 20, 2026
fingerprintinganti-botdetection

Browser fingerprinting is the practice of identifying or classifying a visitor from the signals their browser and connection give off, without cookies. Anti-bot systems use it to decide whether a request comes from a real browser or a script, by checking that every signal layer tells the same story.

TL;DR

  • A fingerprint is not one value. It is a stack of signals read at four layers: network, protocol, browser runtime, and behavior.
  • Anti-bot vendors do not need a unique ID for you. They need consistency: every layer must tell the same story about the same device.
  • Most scrapers get caught by contradiction, not by identity. A Linux TLS stack claiming to be Windows Chrome is a block before any JavaScript runs.
  • Entropy is uneven. Canvas, WebGL, fonts and audio carry most of it. User agent carries almost none since UA reduction, and since 2025 it must agree with the Client Hints the browser sends or it counts against you.
  • New in 2026: the same layered method now fingerprints AI agents. A June 2026 study separated six agent frameworks from humans and from classic crawlers at 97% accuracy using TLS, HTTP and interaction features together.
  • Fix the layers in order: network first, then protocol, then runtime. Patching the runtime while the TLS fingerprint is wrong buys nothing.

The stack at a glance

flowchart TB
    accTitle: Browser fingerprint layers
    accDescr: Four signal layers, network, protocol, runtime and behavior, feed one consistency check that passes, challenges or blocks the request.
    subgraph L1[1. Network]
        A1[IP and ASN]
        A2[TCP options, TTL]
    end
    subgraph L2[2. Protocol]
        B1[TLS ClientHello → JA4]
        B2[HTTP/2 SETTINGS, header order]
    end
    subgraph L3[3. Runtime]
        C1[navigator, screen, timezone]
        C2[canvas, WebGL, audio, fonts]
        C3[integrity: patched functions]
    end
    subgraph L4[4. Behavior]
        D1[pointer paths, scroll, keys]
    end
    L1 --> L2 --> L3 --> L4
    L1 & L2 & L3 & L4 --> S{{Consistency check<br/>across layers}}
    S -->|agree| P[pass]
    S -->|undecided| C[challenge]
    S -->|contradict| X[block]
    class L1 blue
    class L2 violet
    class L3 teal
    class L4 amber
    class S key
    class P ok
    class C warn
    class X bad

Each layer is read at a different moment: the network before the handshake, the protocol during it, the runtime after JavaScript loads, and behavior for the life of the session. The verdict comes from comparing them, not from any one of them.

The four layers

Layer 1, network. Before any HTTP, the server sees your IP address, its autonomous system (datacenter, residential ISP, or mobile carrier), the TCP options and initial window size, and TTL. These hint at the operating system and the hosting type. A datacenter ASN already costs you points.

Layer 2, protocol. The TLS ClientHello announces cipher suites, extensions and their order, supported curves and ALPN values. HTTP/2 adds the SETTINGS frame, window update values and pseudo-header order. Together these identify the HTTP client library with high precision. Chrome, Firefox, Safari, Go's net/http, Python requests and curl all look different here, and no header can hide it.

Layer 3, runtime. Once JavaScript runs, the client script reads hundreds of properties: navigator fields, screen geometry, time zone, languages, installed fonts, media codecs, WebGL vendor and renderer strings, canvas and audio rendering hashes, and the shape of the window object itself. It also checks what is missing or inconsistent: a Chrome user agent with no chrome object, a desktop screen reporting touch points, a timezone that disagrees with the IP's country.

Layer 4, behavior. Mouse movement, scroll cadence, focus and visibility events, keystroke timing, and the time between page load and first interaction. Covered in its own post.

Why consistency beats uniqueness

A common misconception is that the goal of a fingerprint is to identify one user across sessions. Tracking vendors want that. Anti-bot vendors want something simpler: a risk score.

They compare each layer against the population of real browsers that claim the same identity. If a visitor says "Chrome 130 on Windows" at layer 3 but its TLS fingerprint matches Go at layer 2, the score is decided. If every layer agrees but the IP is a known proxy range, the request may still pass with a challenge.

This is why "set a realistic user agent" has been useless advice for years. The user agent is the cheapest signal to fake and therefore the least trusted. Since Chrome reduced the UA string, the detail moved to User-Agent Client Hints (Sec-CH-UA, Sec-CH-UA-Platform, the full-version list on request) and to navigator.userAgentData. A UA string that claims Chrome 150 on Windows next to Client Hints that say Linux is the same contradiction at a different layer, and 2026 detection write-ups list a single UA profile without matching hints among the patches that stopped working in 2024 to 2025.

Where the entropy is

Not all signals are equal. Roughly, in order of distinguishing power for a modern browser:

  1. Canvas and WebGL rendering hashes (GPU, driver, OS font rasterizer).
  2. Font enumeration via measured text widths.
  3. Audio context output hash.
  4. Screen and window geometry combined with device pixel ratio.
  5. Language, timezone and locale set.
  6. Everything else, including the user agent.

The top three are hardware and driver dependent, which is why headless browsers in a datacenter cluster look alike: same virtual GPU, same fonts, same software renderer, thousands of times.

Demonstration: print your own layers

Open any page, paste this in the DevTools console, and read the output. It covers the runtime layer only; the protocol layer is shown in the next post, on TLS and HTTP/2 fingerprints.

(async () => {
  const c = document.createElement('canvas');
  const g = c.getContext('webgl') || c.getContext('experimental-webgl');
  const dbg = g && g.getExtension('WEBGL_debug_renderer_info');
  const ctx2d = c.getContext('2d');
  ctx2d.textBaseline = 'top'; ctx2d.font = '14px Arial';
  ctx2d.fillText('fingerprint \u{1F50D}', 2, 2);
  const canvasHash = [...new Uint8Array(await crypto.subtle.digest('SHA-256',
    new TextEncoder().encode(c.toDataURL())))].slice(0, 8)
    .map(b => b.toString(16).padStart(2, '0')).join('');
  console.table({
    userAgent: navigator.userAgent,
    platform: navigator.platform,
    hardwareConcurrency: navigator.hardwareConcurrency,
    deviceMemory: navigator.deviceMemory,
    languages: navigator.languages.join(','),
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    screen: `${screen.width}x${screen.height}@${devicePixelRatio}`,
    outer: `${outerWidth}x${outerHeight}`,
    touchPoints: navigator.maxTouchPoints,
    webdriver: navigator.webdriver,
    webglVendor: dbg ? g.getParameter(dbg.UNMASKED_VENDOR_WEBGL) : 'n/a',
    webglRenderer: dbg ? g.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : 'n/a',
    canvasHash,
  });
})();

Expected output on a real laptop: a hardware renderer string such as an Apple, NVIDIA, AMD or Intel GPU, webdriver: false, and outer dimensions slightly larger than inner ones because of the browser chrome.

Expected output in headless Chrome without a GPU: a software renderer (SwiftShader or ANGLE over llvmpipe), webdriver: true when driven through automation, and outerWidth of 0 in older builds. Run the same snippet in both and diff the tables. That diff is the detection surface at layer 3.

Read more: the full signal inventory

A reasonably complete list of what a modern client script collects. Vendors differ in weighting, not in inventory.

Group Signals
Navigator userAgent, userAgentData (brands, platform, mobile), platform, vendor, language, languages, hardwareConcurrency, deviceMemory, maxTouchPoints, webdriver, pdfViewerEnabled, plugins and mimeTypes shape, doNotTrack, cookieEnabled
Screen width, height, availWidth, availHeight, colorDepth, pixelDepth, devicePixelRatio, orientation, outer vs inner window sizes
Rendering canvas 2D hash with text and emoji, WebGL parameters and extensions, unmasked vendor and renderer, WebGL2 presence, audio context oscillator hash, CSS media features (color gamut, prefers-* values, hover and pointer)
Fonts measured widths of a probe string in a list of font families versus fallback, and the local font access API where available
Environment timezone and offset, Intl formatting results, Date behavior across DST, performance.now resolution, memory and storage quota estimates
Integrity property descriptors on native functions (toString of patched methods), prototype chain order, error stack formats, presence of automation globals, iframe contentWindow consistency, permission query results against the Notification.permission value
Network from JS WebRTC ICE candidates, connection type and RTT estimates, fetch timing to vendor endpoints
Read more: agents are the new population (2026)

The population being fingerprinted changed in 2025 and 2026. Autonomous browsing agents (Browser Use, OpenAI Operator, Skyvern, AutoGen-based agents, and the browser modes of the large assistants) now make a measurable share of automated traffic. A June 2026 arXiv paper, "Whose Agent Are You?", instrumented a live domain and showed that each framework assembles HTTP requests, sets up TLS and HTTP connections, and drives the browser in a structurally distinct way.

A decision tree over those cross-layer features reached 97% accuracy at telling the frameworks apart and separating them from both humans and legacy crawlers. The authors' point is the same as this post's: single-layer evasion fails because the layers are correlated. Vendors have responded by renaming the category; Forrester's Q2 2026 evaluation is titled "Bot and Agent Trust Management", and the vendors covered in the anti-bot client script post now sell agent identity verification alongside detection.

Read more: how scores are combined

Vendors typically run three passes. First, hard rules: known bad fingerprints, impossible combinations, and signature matches for automation frameworks. These block immediately. Second, a statistical model: how far is this fingerprint from the population seen for the same claimed browser and version, weighted by entropy.

Third, session context: does the fingerprint stay stable across requests, does it match the IP's geography, does the behavior layer look human. The output is a score. Below one threshold the request passes, above another it is blocked, and in between a challenge is served. The challenge tiers are covered in the CAPTCHA post.

Read more: references
  • Eckersley, "How Unique Is Your Web Browser?" (2010), the paper that introduced entropy measurement for fingerprints.
  • Laperdrix et al., "Browser Fingerprinting: A Survey" (2020).
  • The Chromium project's User-Agent reduction notes, which explain why the UA string lost most of its entropy.
  • CreepJS and BrowserLeaks, open test pages that show the collected signals interactively.
  • Kang et al., "Whose Agent Are You? Multi-Layer Fingerprinting and Attribution of Autonomous Web Agents" (arXiv, June 2026).

Frequently asked questions

What is a browser fingerprint?

A browser fingerprint is the combined set of signals a site can read from a visitor: IP and network data, the TLS handshake, JavaScript-visible properties such as screen, fonts, canvas and WebGL output, and behavior. Together they describe a device well enough to recognize it, or to tell a real browser from automation.

Can changing the user agent avoid fingerprinting?

No. The user agent is the cheapest signal to fake and the least trusted. Since Chrome's user-agent reduction the detail lives in Client Hints, and a user agent that contradicts the TLS fingerprint, Client Hints or runtime properties makes detection easier, not harder.

Which fingerprint signals carry the most information?

Canvas and WebGL rendering hashes, font measurements and the audio context hash carry the most, because they depend on the GPU, the driver and the operating system. Screen geometry, language and timezone come next. The user agent carries very little.

How do anti-bot systems use fingerprints?

They compare each layer with real browsers that claim the same identity and score the contradictions. Hard rules block impossible combinations, a statistical model scores the rest, and session context such as IP geography and behavior moves the score until the request passes, gets a challenge, or is blocked.

Sources, checked October 2026

How anti-scraping works

Ten posts on what bot detection measures, how the vendors use it, and what current tools do about it. Each one starts with a TL;DR and ends with an experiment you can run.

  1. Anatomy of a browser fingerprint
  2. TLS and HTTP/2 fingerprints: identified before the first header
  3. Headless tells: how automated Chrome gives itself away
  4. Behavioral biometrics: scoring humans without a CAPTCHA
  5. WebRTC, DNS and proxy leaks: how your real IP escapes
  6. Inside an anti-bot client script: collect, challenge, token
  7. IP reputation and rate models: why slowing down is not enough
  8. The stealth tooling landscape in 2026, with a benchmark you can run
  9. Intercept the internal API instead of scraping the HTML
  10. CAPTCHAs in 2026: the challenge is the fallback, not the gate