Your headless browser is not invisible. It never was.

Puppeteer and Playwright are excellent automation tools. They’re also the two most detected automation frameworks on the internet.

Every major anti-bot system — Akamai, DataDome, PerimeterX, Kasada, Cloudflare — has dedicated detection logic for Puppeteer and Playwright. Not generic “headless browser” detection. Specific, targeted detection for these exact tools and their known artifacts.

If you’re running Puppeteer or Playwright against any site with advanced anti-bot protection, you’re blocked before your script finishes loading the page. Here’s exactly how they catch you, and why the popular “fixes” don’t work.

The detectable artifacts of Puppeteer

Puppeteer launches Chromium (or Chrome) and controls it via the Chrome DevTools Protocol (CDP). This architecture creates dozens of detectable artifacts:

The most obvious signal. When Chrome is controlled by automation, navigator.webdriver returns true. Every anti-bot system checks this.

“But I set it to false!” — Yes, and anti-bot systems know you did. They check how it was set. Overriding it with Object.defineProperty leaves a trace. The property descriptor is different from a browser where webdriver is natively false. DataDome checks the property descriptor, not just the value.

Chrome DevTools Protocol artifacts

CDP itself leaks. When Puppeteer connects to Chrome via CDP, it creates artifacts that don’t exist in a normal browser:

  • Runtime.enable — Puppeteer calls this to set up JavaScript evaluation. It modifies the execution context in ways that JavaScript on the page can detect.
  • Extra execution contexts — CDP creates isolated worlds for evaluating scripts. Anti-bot JavaScript can detect the presence of these additional contexts.
  • console.debug interception — CDP intercepts console methods, changing their behavior in detectable ways.
  • Page lifecycle events — The order and timing of page lifecycle events differ when CDP is controlling the browser vs. normal user browsing.

window.chrome inconsistencies

Real Chrome has a window.chrome object with specific properties: chrome.app, chrome.csi, chrome.loadTimes, chrome.runtime. Puppeteer’s Chromium has a subtly different window.chrome — missing properties, different method behaviors, or absent getters/setters.

Anti-bot scripts enumerate window.chrome thoroughly. They don’t just check if it exists — they test method return values, property descriptors, and prototype chains.

Plugin and mimeType arrays

Real Chrome reports plugins (navigator.plugins) and MIME types (navigator.mimeTypes) from installed extensions and system plugins. Headless Chromium reports empty arrays or minimal entries. Even in headed mode, Puppeteer-launched Chrome typically has fewer plugins than a real user’s browser.

Anti-bot systems maintain distributions of plugin counts and types across real user populations. An empty plugins array is an immediate red flag.

iframe contentWindow behavior

There’s a well-known detection technique using cross-origin iframe access. Real Chrome handles contentWindow access on sandboxed iframes differently than Puppeteer-controlled Chrome. Anti-bot scripts create invisible iframes and test access patterns to detect automation.

The detectable artifacts of Playwright

Playwright has the same CDP-based artifacts as Puppeteer (when using Chromium) plus additional ones:

Playwright-specific browser patches

Playwright maintains custom browser patches for Chromium, Firefox, and WebKit. These patches modify browser behavior to enable automation features. Each patch is detectable:

  • Modified browser user data directory structure
  • Custom command-line flags that leave traces in navigator properties
  • Patched event dispatching that changes event timing characteristics

Browser context isolation

Playwright’s browser context feature (its killer feature for testing) creates detectable patterns. The way contexts share and isolate state differs from how a real browser manages tabs and windows.

Protocol-level differences

Playwright uses an extended version of CDP with custom commands. While these commands aren’t visible to page JavaScript directly, the browser’s response to these commands (modified internal state) can be probed by sophisticated anti-bot scripts.

Why stealth plugins don’t save you

puppeteer-extra-stealth

The most popular stealth solution for Puppeteer. It applies a set of “evasions” that patch known detection vectors:

  • Overrides navigator.webdriver to return false
  • Patches chrome.runtime to appear present
  • Spoofs plugin and mimeType arrays
  • Modifies window.chrome properties
  • Patches iframe detection techniques
  • Hides HeadlessChrome from the User-Agent

In 2022, this was somewhat effective. In 2026, it’s a detection signal itself.

Why? Because the specific combination of patches that puppeteer-extra-stealth applies creates a unique fingerprint. A browser with exactly the properties that stealth patches and none of the unpatched inconsistencies matches a known pattern in anti-bot training data. DataDome has seen millions of stealth-patched Puppeteer sessions. Their ML model recognizes the pattern instantly.

The stealth plugin turns one bot fingerprint into a different — but equally recognizable — bot fingerprint.

playwright-stealth / playwright-extra

Same concept applied to Playwright, same result. The patches are well-known, the resulting fingerprints are in every anti-bot training dataset, and the detection rate is near 100% on advanced systems.

undetected-chromedriver

undetected-chromedriver takes a different approach: it modifies the ChromeDriver binary to remove known detection markers. It patches the cdc_ variables that ChromeDriver injects, removes automation flags, and modifies the driver’s behavior.

On basic anti-bot systems, undetected-chromedriver works reasonably well. On Akamai Bot Manager, DataDome, or PerimeterX? It fails.

The reason: undetected-chromedriver still uses CDP to control the browser. It still creates the CDP artifacts described above. It still runs in an environment (typically a cloud VM) without a real GPU, producing detectable canvas fingerprints. Removing cdc_ variables doesn’t fix any of the deeper detection vectors.

Botright and other “next-gen” stealth tools

Botright combines Playwright with fake human behavior simulation — random mouse movements, typing with delays, natural scrolling. It’s a step in the right direction, but it fails on advanced anti-bot for two reasons:

  1. The “random” mouse movements aren’t human. Real mouse movements follow specific mathematical distributions (Fitts’s Law, minimum jerk principle). Random movements are detectably random. PerimeterX analyzes movement trajectories and classifies them with ML.

  2. The underlying browser is still Playwright. All the CDP artifacts, browser patches, and automation traces remain. Simulating mouse movements on top of a detected browser is like wearing a disguise after you’ve already been identified.

CDP detection: the deepest problem

Chrome DevTools Protocol is the fundamental mechanism that Puppeteer, Playwright, and most automation tools use to control the browser. CDP detection is the hardest problem in browser automation stealth, because CDP modifies the browser’s internal state in ways that are visible to JavaScript on the page.

How CDP is detected

  1. Execution context enumeration: CDP creates additional execution contexts (isolated worlds) that page JavaScript can detect through timing side-channels and error behavior.

  2. Event dispatch differences: When CDP dispatches events (clicks, keystrokes, mouse movements), the event objects have subtle differences from real user-generated events. event.isTrusted is true for CDP-dispatched events, but other properties (timing, composed path, detail values) can differ.

  3. Runtime evaluation traces: CDP’s Runtime.evaluate and Runtime.callFunctionOn modify the JavaScript execution environment. Specific error messages, stack traces, and exception behaviors change when code runs in a CDP-evaluated context.

  4. Network interception artifacts: If you use CDP to intercept network requests (a common pattern), the timing and ordering of requests changes detectably.

Why CDP detection is unfixable

You can’t patch CDP detection with stealth plugins because CDP is the mechanism that stealth plugins use to apply their patches. It’s a paradox: the tool you use to hide automation is itself the strongest signal of automation.

Some projects have attempted to use alternative control mechanisms — X11 event injection, OS-level input simulation, browser extension-based control. These approaches solve CDP detection but introduce their own artifacts, and they’re significantly slower and more fragile than CDP-based automation.

The only solution: stop automating the browser

Every approach described above tries to control a browser and make it look uncontrolled. It’s fundamentally contradictory.

We took a different approach entirely. Instead of automating a browser and hiding the automation, we use real browsers with real user environments. No CDP. No automation protocol. No headless mode. No stealth patches.

Our Chrome browsers:

  • Run with real GPU hardware producing authentic canvas and WebGL fingerprints
  • Have no CDP connections — there’s no automation protocol to detect
  • Expose genuine browser APIs with no patches, no overrides, no spoofed properties
  • Generate authentic TLS fingerprints that match real Chrome exactly
  • Produce real user-like behavioral patterns — not simulated, not random

When DataDome’s detection script runs in our browser, it finds a real Chrome browser with a real GPU, real plugins, real fingerprints, and no automation artifacts. Because there are none.

This is why we achieve 99%+ success rates on sites where Puppeteer and Playwright achieve 0%.

Stop fighting detection. Start being undetectable.

If you’re maintaining a Puppeteer or Playwright scraping pipeline against advanced anti-bot sites, you’re in a losing arms race. Every patch you apply becomes the next detection vector. Every stealth update buys you weeks at best before anti-bot models retrain and catch the new fingerprint.

Try our playground with the URL that’s been blocking your Puppeteer scripts. See what happens when you stop automating browsers and start using real ones.