Skip to content
Blog

Patched Chromium browsers explained

How scrapers patch Chromium to hide automation fingerprints, and why navigator.webdriver checks no longer catch them.

Frederick Jahn
Frederick JahnMarch 25, 2026
Patched Chromium browsers explained

How scrapers patch Chromium's source code to hide automation fingerprints, and why navigator.webdriver checks no longer catch patched browsers.

What is a patched Chromium browser?

A patched Chromium browser is a build with modified source code. The changes remove the fingerprints that mark it as automated. They are compiled in. By the time the binary runs, the tells are already gone. No injected variables. No telltale flags. No odd runtime properties.

Stealth plugins work differently. Tools like puppeteer-extra-stealth run JavaScript at page load. They overwrite those properties after the fact. Runtime shims leave their own traces. Patched builds do not. The thing a shim would hide was never there.

The most used patched projects are undetected-chromedriver, rebrowser-patches, Patchright (a Playwright fork), and nodriver. On the Firefox side, there is camoufox. They patch different things with different force. The premise is the same. Fix the leak in C++, not JavaScript.

Detection used to be simpler. Chrome unified its headful and headless codebases in November 2022. Before that, you could catch most scrapers with one check. You looked for the HeadlessChrome user-agent string. That check still ships in a lot of bot-detection code. It catches almost nothing now.

Why patched Chromium matters now

Launch Puppeteer or Selenium with default settings against a site with real detection. You lose. The reasons are dull and well documented.

The first is navigator.webdriver. The WebDriver spec requires it to be true when automation controls a browser. Any page script can read it in one line. Selenium sets it. Playwright sets it. Puppeteer sets it.

The second is $cdc_asdjflasutopfhvcZLmcfl_. ChromeDriver injects this variable into every page it drives. It is part of how Selenium talks to the browser. The string is fixed. A single regex catches it.

The third is the HeadlessChrome substring in the user agent. Old headless mode still ships it if nobody overrides it. The fourth is a pile of runtime properties. A real browser and a controlled one differ in small ways. Missing plugins. Zero-length languages. The wrong screen.colorDepth. No window.chrome object. None of these alone proves automation. Together they do.

Each fix is a one-line change. So a patched browser takes about an hour to assemble if you know the steps. Most scraper tutorials stop here. But this only beats the checks a 2019 system would run. Modern systems look somewhere else.

Types of patched Chromium techniques

The ecosystem splits into three generations. Each targets a different layer of the automation stack.

First generation: fingerprint surface patches. undetected-chromedriver modifies the Selenium ChromeDriver binary. It renames $cdc_ variables and strips the obvious Selenium tells. Puppeteer and Playwright users run rebrowser-patches. It modifies the library source so it does not enable the CDP Runtime domain on every page load. You apply these patches once to your install. They persist across releases until the upstream code changes enough to break them.

Second generation: protocol-level patches. Patchright is a Playwright fork. It ships as patchright and patchright-python. It runs JavaScript through Page.createIsolatedWorld instead of the default Runtime domain. So the page side never sees the events detection code looks for. The patches live at the C++ level, not as JavaScript wrappers. JavaScript wrappers add their own detectable side effects.

Third generation: post-CDP frameworks. nodriver and its async fork zendriver rebuild browser control using OS-level input. They move the mouse through operating-system APIs. They type through keyboard events. So no CDP session exists for a detector to notice. camoufox does the same for Firefox. It patches the Juggler automation protocol at the C++ layer before compilation. So navigator.hardwareConcurrency, WebGL renderer strings, and screen geometry all match a real browser.

The shift is clear. Modern anti-detect frameworks are more targeted. Rather than apply dozens of evasions, they focus on a smaller set of powerful, low-level signals. Most of those come from the browser automation protocols themselves.

Most scrapers do not maintain their own Chromium fork. A fork would give the cleanest bypass. The reason they skip it is cadence. Chrome ships a new milestone every four weeks (Chromium project). Each release moves code around, renames internals, and adds new detection surfaces. A fork that is not rebased onto the new release soon stops building at all.

The patches are small. Often a handful of lines in a specific Runtime or Page handler. Rebasing them onto each new Chromium is not small. Every four weeks the fork owner has to reapply the changes against new source. Then retest them against live bot-detection vendors. Then ship a new binary before the old one ages out. camoufox shows what happens when one person carries the weight. Its long maintenance gap through most of 2025 left the project shipping almost no updates.

So the commercial market fills the vacuum. Bright Data, Browserless, and similar services run patched Chromium as a hosted API. You point Playwright at their WebSocket endpoint. They handle the rebasing. The tradeoff is that these services have their own tells. Independent benchmarks find the CDP automation flag is still readable from inside the browser in every session they serve.

How patched Chromium works

Puppeteer, Playwright, and Selenium all talk to Chromium through one channel. It is the Chrome DevTools Protocol, or CDP. CDP is a WebSocket interface the browser exposes for debugging. Automation frameworks use it to drive clicks, run scripts, and read the DOM. About 95% of automated actions run through Page.evaluate. That is CDP's way of running JavaScript inside the page.

Running Page.evaluate requires enabling the CDP Runtime domain. Enabling it emits events the page can observe. For years, detection code used a simple trick. It planted an error object with a custom getter on .stack. It called console.debug() on that object. Then it watched for the getter to fire. A firing getter meant the Runtime domain was serializing the object across the WebSocket.

Then two V8 commits killed the trick in May 2025. One was titled 'Avoid error side effects in DevTools'. It landed on May 7. 'Apply getter guard throughout error preview' followed on May 9. Together they stopped the browser from running user-defined getters during error preview. The console.debug check went from reliable to useless. Most vendors did not notice for months.

The fallout was immediate. Even the simplest bots, the ones that used to get caught, no longer trigger the CDP signal.

Depend on that one signal and you run blind against any automation built after May 2025. And CDP is where modern patched browsers spend most of their effort. The ecosystem is moving down the stack. Every time a detection signal gets published, the patches move one layer closer to the bare metal.

How to identify patched browsers

Headless fingerprint detection is easy to beat. Behavioral detection is not.

The signals that survive patching depend on how the browser is used. Not on what it looks like at page load. Mouse paths that travel in straight lines between elements. Identical typing cadence across sessions. Forms filled faster than a human could read them. Session shapes that repeat at request counts no real user population produces. None of these show up in a single request. That is why page-load fingerprinting misses them.

The scale of automation forces this shift. Our data shows about 30% of all internet traffic came from bots in 2025. During the first week of March 2025, over 94% of authentication requests came from bots. We see more than 10,000 unique IPs per day running headless Chrome for scraping and carding. The traffic splits roughly half-and-half between headless and headful mode. At that volume, a system that scores one request in isolation is scoring noise.

By early 2025, puppeteer-extra-stealth no longer reliably bypassed modern edge detection. It had once been the default way to mask automation. Maintainers stopped updating it. The tools that replaced it share one property. They hide page-load signals but cannot hide a session that acts like a bot. The detection that works now watches behavior over time. Not a flag at page load.

How to respond when patches adapt

Patched browsers are an arms race. Every time a vendor publishes a new signal, the patch authors move one layer closer to the bare metal. The question for any defender is simple. Does the detection stack refresh at the same cadence?

Monitor the signals that still fire against known automation. When a reliable check goes dark, audit the rest of the stack against fresh traffic. The console.debug getter trick did exactly that after V8's May 2025 commits. Behavioral signals hold longer than fingerprint signals. They depend on how the automation uses the browser, not what the browser is. Refresh the fingerprint database on a fixed cadence. New Chromium, new Patchright release, new CDP shift. Then pair it with a behavioral layer that a single V8 commit cannot turn off.

Key takeaways

  • Patched Chromium builds remove automation fingerprints in C++ before compilation, not with JavaScript at page load. The HeadlessChrome user-agent check and navigator.webdriver flag catch old tooling and nothing else.
  • Three generations of patches move down the stack. Fingerprint surface (undetected-chromedriver, rebrowser-patches), protocol-level (Patchright, isolated worlds), and post-CDP (nodriver, zendriver, camoufox). Each targets a different detection layer.
  • The May 2025 V8 commits that stopped user-defined getters in error preview killed the console.debug CDP check overnight. Vendors that relied on that single signal ran blind for months.
  • What still works is multi-signal detection. It combines request-level fingerprints with behavioral patterns across a session. When 94% of authentication requests are automated, a single-flag detector is already behind. Centinel runs runtime signals against 1,600+ crawler fingerprints plus behavioral analysis at the edge.