---
title: "navigator.webdriver and CDP detection: 9 checks Reddit runs"
description: "What Reddit's 9 platform health checks read, run verbatim on 8 September 2026 against a person, a bare CDP driver and our browser, and why webdriver is 1 of 17."
canonical: "https://scalebrowser.net/blog/webdriver-cdp-detection"
last_modified: "2026-10-18"
published: "2026-10-18"
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.

# navigator.webdriver and CDP detection: 9 checks Reddit runs

> What Reddit's 9 platform health checks read, run verbatim on 8 September 2026 against a person, a bare CDP driver and our browser, and why webdriver is 1 of 17.

Setting `navigator.webdriver` to false is the first thing every automation guide recommends, and it is the one change a detection script expects. The flag is a single value; the software that drives the browser leaves traces in several other places, and some of them grow when you hide the first one. We took the checks Reddit ships to every visitor, ran them unchanged against a person, a bare driver and our own browser, and counted what each one caught.

## What is control plane detection?

Control plane detection is the search for the software that drives a browser, as opposed to the search for a browser that lies about itself. The W3C [WebDriver specification](https://w3c.github.io/webdriver/) created the most famous signal on purpose: `navigator.webdriver` "defines a standard way for co-operating user agents to inform the document that it is controlled by WebDriver". A driver that does not want to co-operate still leaves three kinds of trace behind.

- **Markers**: the flag itself, globals a driver injects, such as the `cdc_` prefix of chromedriver or Playwright's binding names, and the marks left by hiding them.
- **Protocol side effects**: behaviour that changes because a DevTools client is attached, such as a console that reports to a listener or a tab that is attached but not in front.
- **Input without a hand**: a click that arrives with no pointer movement before it, or key events that skip a step a keyboard always produces.

## Which 9 checks does Reddit run?

Reddit runs 9 checks in a module it calls `platform_health`, and all 9 sit in one self-contained block of 18,475 bytes in the bundle every visitor downloads. On 8 September 2026 we lifted that block byte for byte, verified it against its checksum, and ran it on a local page on one Windows machine against four setups: a person, a Scalebrowser profile, and a bare CDP driver with and without Chrome's automation switch. No Reddit page was loaded and no account was used. Each setup ran once, for about 7 minutes, because one check counts five visible minutes.

<Evidence source="Reddit platform_health block, 18,475 bytes, run verbatim on a local page, Scalebrowser engine 150 and Chrome 152 on one Windows machine, 1 run per setup, 8 September 2026">

| Check | What it reads | Person | Scalebrowser profile | Bare CDP driver |
| --- | --- | --- | --- | --- |
| h1 | milliseconds since the page started, when the module registers | 663 | 333 | 521 to 553 |
| a1 | 17 automation markers, including signs that a marker was hidden | none | none | headless user agent, plus webdriver with the automation switch |
| e1 | plugins, PDF viewer, WebGL renderer class, notification permission | full plugin list, renderer "other" | same as person | same as person |
| r1 | 5 navigator values, compared with a fresh hidden iframe | 5 of 5 same | 5 of 5 same | 5 of 5 same |
| b1 | battery values and events over 5 visible minutes | readings taken | same as person | same as person |
| c1 | a console trap that fires when a DevTools client listens | not matched | not matched | not matched |
| m1 | pointer path before each click | 21 clicks, 5 to 65 samples | 5 clicks, 5 to 10 samples | no click accepted |
| m2 | the same, with the geometry of the click target | 21 clicks | 5 clicks | no click accepted |
| m3 | every click, with its wider context | 23 clicks, 4 without a path | 6 clicks, 1 without a path | 4 clicks, 4 without a path |

</Evidence>

Three results carry the case. The bare driver was caught on markers in a1 and on input in m1 to m3: none of its 4 clicks had a single pointer sample before it. The Scalebrowser profile raised no marker, and 5 of its 6 clicks came with a path of 5 to 10 samples.

The one click without a full path was a second click on the same spot, where no pointer moves in between. It still carried 2 samples, where the bare driver's carried 0. The person produced 4 such clicks out of 23, so this is a pattern of people rather than of drivers. What does separate our clicks from the person's is the spread: 5 to 10 samples against 5 to 65. Every single value lies within the human range, and whether Reddit's server scores the narrower spread is open; this bench cannot see the server.

c1 teaches the most about the bench itself. The bare driver had the protocol's Runtime domain enabled and was receiving console events, and the trap still did not fire in Chrome 152. A clean "not matched" for your own browser there says nothing about your own browser, which is why a bench needs a positive control before any clean result counts.

## Is navigator.webdriver still the main signal?

No. `navigator.webdriver` is one of 17 markers in one of 9 checks, and 5 of the 17 look for traces of hiding: a deleted flag, an altered property, a plugin list or permission object with the wrong shape, and client hints that disagree with the user agent. A driver that only removes the flag has swapped one marker for another. The grid that hardwareConcurrency and deviceMemory values must sit on is covered in hardwareConcurrency and deviceMemory. The rounding of navigator.connection values is covered in navigator.connection.

The larger signal sits below the page. Ian L. Paterson ran [7 tools against 31 targets](https://ianlpaterson.com/blog/anti-detect-browser-benchmark-patchright-nodriver-curl-cffi/), 651 verdicts over 3 sweeps, and concluded that "Playwright forks fail regardless of patch quality". In his words: "A fingerprint-patch tool can rewrite navigator properties all day without touching this layer." The only tool with no blocked target drove Chrome over CDP directly. That is the layer [how sites detect bots](/blog/bot-detection) calls the control plane, and it sits between the connection and the input. The signals that give a proxy exit away are covered in Proxy detection.

## What does the Chrome DevTools Protocol expose?

The Chrome DevTools Protocol exposes nothing to the page directly; the page sees what a driver does with it. A tab that is attached but not in front reports `visibilityState` "hidden", renders at 0 frames per second and swallows input while the driver is told the click was dispatched, and real Chrome behaves the same way. A tab the driver creates arrives without a `Referer` header and with `Sec-Fetch-Site: none`, unlike a tab a person opens from a link. The Chrome DevTools Protocol article covers these one by one, and the [CDP page of the documentation](/docs/cdp) describes the connection a Scalebrowser profile offers.

### Which stealth tools act on this layer?

[Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) and [rebrowser](https://github.com/rebrowser/rebrowser-patches) patch [Playwright](https://playwright.dev/)'s protocol leaks, and [nodriver](https://github.com/ultrafunkamsterdam/nodriver) drops the Playwright layer altogether; in Paterson's benchmark rebrowser-playwright was blocked on the same targets as unpatched Playwright. The comparison of [stealth tools for automation](/blog/stealth-browser) sets all seven side by side.
