---
title: "Bot detection in 2026: the 4 layers that flag an agent's browser"
description: "How bot detection reads a browser on 4 layers before and after the page loads, how the layers add up to one verdict, and where our own runs went wrong."
canonical: "https://scalebrowser.net/blog/bot-detection"
last_modified: "2026-09-25"
published: "2026-09-25"
author: "davide"
category: "detection"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://scalebrowser.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Bot detection in 2026: the 4 layers that flag an agent's browser

> How bot detection reads a browser on 4 layers before and after the page loads, how the layers add up to one verdict, and where our own runs went wrong.

You changed the user agent, removed `navigator.webdriver` and added a [stealth plugin](/blog/puppeteer-stealth), and the site still answers with a challenge or an empty page. That is the normal result, because a modern bot defence does not look for one tell. It reads the browser on several layers, some of them before the first line of the page's own code runs, and decides per request. This guide walks those layers in the order a site sees them and names, for each, a value we measured and got wrong or right.

## What is bot detection?

Bot detection is the part of a website's defence that decides, per request, whether a browser is driven by a person or by software. Cloudflare, DataDome and HUMAN Security are three of the vendors that sell it. Cloudflare's [bot score documentation](https://developers.cloudflare.com/bots/concepts/bot-score/) describes the output plainly: "a score from 1 to 99 that indicates how likely that request came from a bot", where 99 means "quite certain the request came from a human".

## Which 4 layers does bot detection read?

A defence reads four layers, and they arrive in a fixed order during one page load. Each layer below carries one value from our own measurements.

1. **The connection, before any script.** The TLS ClientHello becomes a JA4 string under the [FoxIO specification](https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md), and HTTP headers arrive with it. Our Chromium 151 build sent 16 TLS extensions where branded Chrome 151 sent 17.
2. **The JavaScript fingerprint.** Canvas, WebGL, fonts, screen and timezone have to describe one plausible device. [Seeded canvas noise](/blog/canvas-fingerprinting) gave 5 of 5 profiles on one machine distinct hashes and got the canvas flagged as manipulated.
3. **The control plane.** The way the browser is driven leaves its own traces. Reddit ships 9 platform health checks in its bundle, and lifted verbatim they catch a bare CDP driver.
4. **The input.** Pointer paths and keystroke timing are compared with people. An injected hand tremor made our paths reverse direction 17 times as often as a person on the same bench.

## How do the 4 layers add up to one verdict?

The layers add up to a score per request, and the site's own configuration decides what that score triggers. Cloudflare names four engines behind its score, among them heuristics, machine learning and JavaScript detections, which "catches headless browsers (browsers controlled by software, with no visible window or human operator)". A single contradiction on one layer is often enough to tip it; in [browser fingerprinting](/blog/browser-fingerprinting), that is two values that describe different machines. The table below lists four cases where one changed value changed the outcome.

<Evidence source="Scalebrowser measurement benches and agent runs, August and September 2026">

| One changed value | What changed | What the site did |
| --- | --- | --- |
| Accept-Language header, PayPal with DataDome, 4 runs | a quality weight in the header | block page against login page |
| TLS extension count, our build against Chrome 151, 3 runs | 16 against 17 extensions | a different JA4 before any script |
| hCaptcha site key, same browser and minute | third-party key against hCaptcha's own demo | a token against no token |
| Exit address, reCAPTCHA v2 checkbox, 191 measurements | bought mobile exits against one home line | 18 percent through against 0 of 70 |

</Evidence>

The last row matters most for anyone who tunes a browser: profile maturity, the thing most people try next, had no measurable effect in the same series (p = 0.60).

### What happens before the page loads?

Before the page loads, the server has already read the TLS handshake and the order and values of the HTTP headers. A script cannot change either, and CDP cannot even show you the header order, because it hands back a normalised object after the request is sent. Our 151 build sent the [JA4 TLS fingerprint](/blog/ja4-tls-fingerprint) of Chrome 150 while its user agent said 151, which a server that knows the Chrome population can see without running any code.

### How is the control plane detected?

The control plane is detected through what the driver does to the page, not through what the page asks the browser. Ian L. Paterson ran 7 tools against 31 targets in a [benchmark published in May 2026](https://ianlpaterson.com/blog/anti-detect-browser-benchmark-patchright-nodriver-curl-cffi/) and found that "Playwright forks fail regardless of patch quality"; the only tool without a blocked target drove Chrome over CDP directly. Removing `navigator.webdriver` alone leaves every other trace in place.

### How is behaviour scored?

Behaviour is scored against the distribution of real people, which is what [behavioral biometrics](/blog/behavioral-biometrics) measure, and adding randomness moves a browser further from it, not closer. The tremor model we shipped produced 4.5 direction reversals per 100 pointer samples where a person produced 0.27.

### How do proxies change the verdict?

A proxy changes the one input no browser patch reaches: the reputation of the address the request comes from. Over 191 reCAPTCHA measurements on 9 September 2026, bought mobile exits let 18 percent of runs through without a puzzle and a single residential line 0 of 70, while the age and history of the profile changed nothing measurable.

<ProductMention />

#### How do Cloudflare and DataDome apply these layers?

Cloudflare combines the layers in its bot score, and its [Turnstile widget](/blog/cloudflare-turnstile) in managed mode chooses between a silent check and a checkbox "based on visitor risk level", as Cloudflare's [widget documentation](https://developers.cloudflare.com/turnstile/concepts/widget/) puts it. DataDome answers a request it distrusts with a block or captcha page of its own, and in our runs it decided on a header that arrives before any script.

#### What did DataDome react to in our runs?

DataDome reacted to one header. With `de-AT,de;q=0.9` the browser got DataDome's captcha page with 3 cookies, with `de-AT,de` the PayPal login page with 19 cookies, in 4 runs alternating A, B, B, A with zero spread. The block page named the IP address as its reason, which was misleading: real Chrome on the same line got the login page, and the header was the cause.

## Where does the agent's browser fit in?

The agent's browser fits in as the thing all four layers are read from, whether it is a fresh test browser or an [AI agent browser](/blog/ai-agent-browser) with a profile of its own. Measure it layer by layer against a real browser on the same machine and the same exit, never against a number on a [test page](/blog/browser-fingerprint-test) alone. The [page on testing your setup](/docs/verify) lists four ways to see what a page reads, including a detector audit that runs against a live profile.
