Skip to content
Ward

navigator.webdriver is true on any Chromium started with a debugging port

No automation flag, no client attached, no WebDriver anywhere. One switch sets the property, measured on Chrome 151.0.7922.76 across two launches.

4 min readDivyansh Singh

navigator.webdriver is the first thing every bot-detection script reads, and it has a reputation for being the naive check - the one that catches an unconfigured Selenium script and nothing else. That reputation is roughly right about what it catches and completely wrong about what sets it.

It is not set by WebDriver. It is set by the remote debugging port.

What was measured

Two headed Chrome 151.0.7922.76 launches on the same Windows 11 machine, same clean profile directory, same page, differing in exactly one switch.

Launch--remote-debugging-portnavigator.webdriver
AAbsentfalse
BPresent, port chosen by the browsertrue

Launch B had no WebDriver anywhere in the picture: no driver binary, no automation extension, no --enable-automation, and - for the second run - no protocol client connected at any point. The port was opened and left alone. The property was still true on the first page load.

This is worth internalising because it inverts the usual mental model. The property is not reporting "something is driving this browser". It is reporting "this browser was started in a way that would let something drive it".

Why that is awkward for a profile manager

Every serious tool in this category applies its device identity over the DevTools Protocol, and the protocol needs the port. There is no version of applying a coherent identity before page script runs that does not involve opening it.

So the port is not optional, which means the property is true, which means the first check every detection script runs would fail on every profile - unless the property is corrected along with everything else.

Ward corrects it. On every profile, whatever the per-surface policy says, because there is no coherent reading of "report this surface truthfully" that includes advertising the mechanism the profile is applied with. It is the one value that is not policy driven, and the reason is written next to the code that does it.

What correcting it does and does not achieve

This is the part that gets overclaimed elsewhere, so here it is with the edges on.

What it achieves. The naive read returns false. That is genuinely the check most scripts run, it is run first, and it is often run alone.

What it does not achieve. A script that looks at how the property answers, rather than at what it answers, is reading a different question. A property that has been redefined has a different descriptor shape from one the engine provides. Functions carry evidence of having been replaced. Prototype chains gain things that were not there. None of that is specific to this property, and all of it is why this site's documentation says that a browser not patched at the source level is visible to inspection and never says otherwise.

The honest summary is that correcting navigator.webdriver answers the question that is asked constantly and does not answer the question that is asked carefully. Both of those are true at once, and a vendor telling you only the first half is selling you the half that is easy.

Two practical consequences

If you are building automation: do not treat the property as a proxy for whether you were noticed. It tells you how your browser was launched. Testing your setup by reading it and seeing false tells you that one line of your configuration worked.

If you are writing detection: it is worth reading precisely because it is cheap and because plenty of traffic does not bother to correct it. But it will produce false positives on legitimate developer traffic, remote debugging sessions, accessibility tooling and anything else that opens the port for a reason that has nothing to do with automation. The port is a developer feature, and treating it as an accusation is how you block people using their own browsers.

The general shape

Most of the measurements that started this journal have the same structure: a value that everybody treats as reporting one thing is actually reporting another, and the difference only shows up when you change one variable at a time and read the result rather than the documentation.

The method is not sophisticated. Launch twice, change one switch, read the page. What makes it rare is that it takes longer than repeating what everybody already believes, and the belief is usually close enough to be difficult to disprove by argument.

How this post was checked

Data source
Two headed Chrome 151.0.7922.76 launches on Windows 11 differing only in the presence of the remote debugging port switch, reading the property from a live page
Measured with
The Chrome DevTools Protocol, and a second launch driven from the command line with no protocol client attached at all
The claim
The property is set by the debugging port alone, which makes it a signal about how a browser was started rather than about whether it is being driven.

Divyansh Singh

Builds Ward at Digital Heroes

Divyansh Singh builds Ward, a Windows manager for many isolated browser profiles, at Digital Heroes. Most of his week is spent in the Chromium command line, the DevTools Protocol and Windows process behaviour. Every measurement quoted in these posts was taken on the machine Ward is built and tested on, with the browser or tool version written down beside the result, and the ones that contradicted what the documentation said are the ones that became posts.

Last reviewed . Corrections and bug reports: support@digitalheroes.co.in.

Ward is the desktop application these measurements came out of

Many browser profiles on one PC, each with its own proxy and its own device identity. Free plan, free account, nothing to buy today.

Related