Anti-Bot

What Is navigator.webdriver Detection?

What Is navigator.webdriver Detection? — conceptual illustration
On this page

navigator.webdriver is a standardized boolean that returns true when the browser is being controlled by automation. Think of it as a built-in honesty flag: any browser driven by software, instead of a person, is supposed to admit it. It is defined by the W3C WebDriver spec and set by Selenium, Playwright, Puppeteer, and any CDP-driven browser (one controlled through Chrome's DevTools Protocol) launched in automation mode. Because reading it is a single property access, it is the very first and cheapest check almost every anti-bot script runs. Hiding it is table stakes - necessary to not fail instantly, but far from sufficient, because dozens of harder signals remain.

Quick facts

SpecW3C WebDriver - navigator.webdriver is true under automation
Set bySelenium, Playwright, Puppeteer, ChromeDriver, any --enable-automation launch
Cost to checkOne property read - the cheapest bot signal that exists
Spoof tellA JS-defined getter fails Function.toString() inspection
RealityHiding it is necessary but never sufficient

Where the flag comes from

The WebDriver standard - the W3C rulebook for how software remote-controls a browser - requires a conforming automation session to set navigator.webdriver to true, so that pages can tell they are being driven. Chrome sets it when launched with the --enable-automation switch or controlled via the DevTools Protocol; Firefox sets it under Marionette (Firefox's automation engine); every mainstream automation framework triggers it by default. On a normal human browser session it is false.

That makes it a perfect first-pass filter: one line, if (navigator.webdriver) flag(), catches every scraper that has not specifically dealt with it - which is a surprising number, because many tutorials never mention it.

Why naive hiding gets caught anyway

The obvious fix is to overwrite the property: Object.defineProperty(navigator, "webdriver", { get: () => false }). This makes the value read false, but it introduces new, detectable artifacts (telltale leftovers of the tampering):

  • The getter - the small function that runs when the value is read - is now ordinary JavaScript. Object.getOwnPropertyDescriptor(navigator, "webdriver").get.toString() returns the patch source instead of "[native code]" (the marker browsers show for built-in functions) - caught by Function.toString() inspection.
  • On real Chrome, webdriver lives on Navigator.prototype (the shared blueprint all navigator objects inherit from), not as an own-property of the navigator instance. Defining it on the instance changes where it appears in the prototype chain - itself a tell.
  • If the patch runs after page scripts, there is a race where the real value is briefly observable.

So spoofing the value with JavaScript trades one obvious signal for a subtler one. The clean fix is to launch the browser so the flag is never set - excluding the enable-automation switch - or to patch at the engine level so the property reads false natively.

Necessary but not sufficient

The most important thing to understand about navigator.webdriver is its place in the detection stack: it is the floor, not the ceiling. Passing it means you are not in the bottom tier of trivially-detectable bots. It tells a vendor nothing reassuring - real users pass it too - so failing it is fatal but passing it earns you nothing. Serious anti-bot systems (Kasada, DataDome, Akamai) treat it as a checkbox and move on to TLS fingerprints (clues from the https handshake), canvas/WebGL/audio coherence (whether those graphics and sound APIs all describe the same machine), behaviour, and the harder probes.

This is why tooling that only hides navigator.webdriver (some minimal patching scripts) still gets blocked everywhere that matters. The flag is the first gate; the real contest is the coherent fingerprint behind it. Engine-level browsers launch with the flag genuinely absent and harden the rest.

Code example

javascript
// The cheapest bot check in existence
if (navigator.webdriver) {
  // Block-grade for the bottom tier of scrapers.
}

// Naive hide - works on the VALUE but adds a new tell
Object.defineProperty(navigator, 'webdriver', { get: () => false });

// How a vendor catches the naive hide:
const d = Object.getOwnPropertyDescriptor(navigator, 'webdriver');
const patched = d && d.get &&
  !Function.prototype.toString.call(d.get).includes('[native code]');
// patched === true  ->  the getter is JS, not native  ->  flagged

// Real fix: launch without the automation switch so the flag is never set,
// e.g. Puppeteer: ignoreDefaultArgs: ['--enable-automation'],
// or use an engine that reports webdriver=false natively.

Related terms

What Is Headless Browser Detection?
Headless browser detection is the set of probes anti-bot systems use to distinguish a headless or instrumented Chrome session from a real us…
What Is Function.toString() Inspection?
Function.prototype.toString() inspection is a technique anti-bot scripts use to identify JavaScript functions that have been modified at run…
How Browser Fingerprinting Works
Browser fingerprinting is how a site combines signals — canvas, WebGL, audio, fonts, navigator probes, TLS (the encryption layer behind http…
What Is the Chrome DevTools Protocol (CDP)?
The Chrome DevTools Protocol (CDP) is the low-level interface for instrumenting and controlling Chromium-based browsers. Low-level means it …
What Is Anti-Bot Detection?
Anti-bot detection is the set of techniques websites use to tell automated traffic apart from real human visitors — and then block, challeng…
What Is CDP Detection?
CDP detection is the family of techniques anti-bot scripts use to tell that a browser is being driven through the Chrome DevTools Protocol (…
What Is Browser Fingerprinting?
Browser fingerprinting is a technique that identifies and tracks a visitor by combining dozens of small, observable characteristics of their…
Anti-Bot Vendor Detection Cheatsheet
A useful first step when working with any protected site you are authorized to access is identifying which anti-bot vendor sits in front of …
How Do Websites Detect Web Scrapers?
Websites spot scrapers by gathering hundreds of small clues about each visitor, then scoring how human the whole picture looks. No single cl…
How Does toString() Reveal a Hooked Function?
Calling toString() on a native browser function returns a fixed marker -- "function name() { [native code] }" -- while a JavaScript wrapper …

Concept map

How navigator.webdriver connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Does the navigator.webdriver flag tell an anti-bot system everything?

No. It clears the single cheapest check, which is necessary, but real users pass it too, so passing earns no trust. Every serious anti-bot system then evaluates TLS fingerprints, canvas/WebGL/audio coherence, behaviour, and harder probes. The flag is the floor of what detection looks at, not the ceiling.

Why does Object.defineProperty on navigator.webdriver still get caught?

Because the replacement getter is a JavaScript function whose toString() returns the patch source instead of [native code] (the marker for built-in functions), and because the property ends up on the navigator instance rather than on Navigator.prototype where it lives in real Chrome. Both are detectable. Launching without the automation switch avoids creating the flag at all.

How do I launch a browser so navigator.webdriver is false from the start?

Exclude the automation switch - for example Puppeteer with ignoreDefaultArgs: ["--enable-automation"], or use a build/engine that does not set the flag. Engine-level anti-detect browsers report it false natively, so there is no JS getter to inspect.

Last updated: 2026-05-31