# Why is navigator.webdriver true? And what false proves

> navigator.webdriver is true because automation set it on purpose. We measured a debug-attached Chrome reading true and still scoring 100 on IPHey.

Source: https://mosaicbrowser.com/blog/why-navigator-webdriver-true

## Why navigator.webdriver is true, and why false proves little

navigator.webdriver is true because automation set it on purpose. We measured a debug-attached Chrome reading true and still scoring 100 on IPHey.

![webdriver true versus webdriver false, each name in its own tile: true and trusted, false and flagged](https://mosaicbrowser.com/covers/why-navigator-webdriver-true.webp)

`navigator.webdriver` is true because the browser is under automation and is saying so on purpose. The W3C WebDriver specification defines a "webdriver-active" flag that is set when the browser is under remote control, and `navigator.webdriver` returns it. So when Selenium, Playwright, Puppeteer or ChromeDriver starts Chrome, the page is told. It is one bit, though, and our own measurements show how little it decides: a stock Chrome reading `true` scored 100 on a detector that marked our profile, reading `false`, as unreliable.

## What turns it on

In Chrome the flag comes from a few places. A launch with `--enable-automation` sets it, and ChromeDriver passes that switch, which is also what puts up the "Chrome is being controlled by automated test software" bar. Headless launches set it. MDN also lists a remote debugging port of 0. And a DevTools client can turn it on directly for a page it is attached to.

Firefox has its own path: the Marionette preference or the `--marionette` switch. The idea is the same across browsers. A test suite can check the value and take a different path, such as not sending analytics from a test run, which is why the standard wants it there at all.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## The measurement where true scored better

We spent four days chasing one IPHey verdict, "inconsistent browser fingerprint", on our engine. To find out whether it was ours, we compared a Mosaic profile with branded Google Chrome 153 on one machine, no proxy, minutes apart:

```
branded Google Chrome 153   Trustworthy, MX 100/100
our profile                 Unreliable,  MX  90/100

```

Then we compared what our binary and the branded Chrome exposed. They matched on the `window.chrome` object, the CSS property set, plugins, Intl data, codecs, WebGL and its parameters, the canvas hash, text metrics, fonts, screen, media queries, audio, and every request header. Two differences were left. One was scrollbar width. The other was `navigator.webdriver`: ours read `false`, and the Chrome control read `true`, because it was debug-attached so we could read it.

So the browser announcing it was automated got the clean score, and the one that said it was not got flagged. Whatever IPHey was reacting to, it was not this bit. The real cause turned out to be somewhere else entirely: the Widevine module handing out a per-install certificate, which [the IPHey whale signal](https://mosaicbrowser.com/blog/iphey-whale-signal) traces. The webdriver difference was on the list the whole time and explained nothing.

There was a second lesson in the same run. Our first control was Chrome for Testing, Google's own unbranded build, and it contradicts itself (its brand list says Chromium while its User-Agent says Chrome), so it could not tell us anything about ours ([why the control has to be Google's build](https://mosaicbrowser.com/blog/the-control-is-googles-build)).

## Why false proves little

A detector that wants to know whether a program is driving the page has many better questions than this one. Whether a DevTools client has switched on the Runtime domain is one: on the detector we tested, one `Runtime.enable` flipped a clean profile to `isBot=true`, on an engine where `navigator.webdriver` reads `false` ([Runtime.enable detection](https://mosaicbrowser.com/blog/runtime-enable-detection) has the table). How the mouse moves, whether clicks carry real coordinates, how fast forms fill and whether the browser's values agree with each other are others.

Setting the value to `false` is also easy to get wrong. The common JavaScript fix, `Object.defineProperty(navigator, 'webdriver', ...)`, puts an own property on the `navigator` object, where real Chrome has it on `Navigator.prototype`. And the getter it installs prints its own source when a page calls `toString()` on it, where a real one prints native code. A fix that a page can see is a stronger signal than the value it was hiding.

## What our engine does

Tesseract Chrome, our own Chromium, carries a small patch, `0010-webdriver.patch`, in `navigator.cc`. Stock Chrome returns `true` whenever the AutomationControlled runtime feature is on, which is what the launch switches turn on. The patch removes that early return and leaves the DevTools override in place. A profile that Mosaic launches and drives therefore reads `false` from C++, on the prototype, with a native getter, in every frame, with no script involved. Mosaic's profile audit has a row for it, `navigator.webdriver is false`.

We patch it because a browser a person is using reports `false`, and a profile is meant to look like that browser. But it is the cheapest of the automation checks, not the one that matters most, and we would not trust a tool whose whole answer to automation detection was this one value. The values it sits beside are where the work is ([what we change inside the engine](https://mosaicbrowser.com/features/real-fingerprints)).

## Questions

### Why is navigator.webdriver true in my browser?

Because something started or attached to the browser as an automation client. The WebDriver standard asks browsers to report that, and Chrome turns it on for launches with --enable-automation, which ChromeDriver passes, for headless runs, and when a DevTools client asks for it.

### Is navigator.webdriver true a bug?

No. It is the documented way for a page to know it is being driven, so test suites can skip analytics or animations. A browser under automation that reported false would be the unusual one.

### Does setting navigator.webdriver to false hide automation?

No. It removes one value from a long list. Overriding it in JavaScript also leaves its own traces, such as a getter that is not native code or a property on the wrong object.

### How does Mosaic handle navigator.webdriver?

A patch in Tesseract Chrome, Mosaic's own Chromium, stops launch switches from turning it on, so a profile Mosaic drives reads false without any script changing it. The profile audit checks that it does.

## Related

[Runtime.enable flips a detector, so we don't call it](https://mosaicbrowser.com/blog/runtime-enable-detection): Runtime.enable detection, measured: one DevTools command turned a clean Mosaic profile into isBot=true on deviceandbrowserinfo. We removed all five calls.

[The control that settles "is it our patches?"](https://mosaicbrowser.com/blog/the-control-is-googles-build): Unpatched Chrome for Testing got the same IPHey verdict as our engine, so our patches were not the cause. Its voice list still differs from Chrome's.

[IPHey's "(whale)" signal, and what it was really about](https://mosaicbrowser.com/blog/iphey-whale-signal): IPHey's inconsistent browser fingerprint (whale) looked like a version problem. We followed it through branding and JavaScript to a Widevine certificate.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.

[What is an anti-detect browser?](https://mosaicbrowser.com/guides/what-is-an-anti-detect-browser): An anti-detect browser gives each profile its own fingerprint, proxy and cookies, so sites see separate visitors. What it changes, how, and its limits.

[A detector that never answered because its tab was in the background](https://mosaicbrowser.com/blog/requestanimationframe-background-tab): Background tab throttling stops requestAnimationFrame. Our test tab was in the background, two detectors never answered, and Page.bringToFront fixed both.



[Headless is not a way to hide, it is a way to be seen](https://mosaicbrowser.com/blog/headless-is-a-signal): Headless Chrome names itself HeadlessChrome in its user agent and drops the real GPU. Why our background work runs headed, off-screen, instead.

## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
