Your scraper can spoof every JavaScript fingerprint and still get a CAPTCHA on the first request. CDP detection is the reason. Cloudflare and DataDome do not need to inspect your navigator.webdriver flag or your canvas noise — they watch for the fact that a Chrome DevTools client is attached to the browser at all, and Playwright and Puppeteer announce themselves the instant they try to run code.
That is the part most teams miss. You are not detected for how your automation behaves. You are detected for what it is.
The short version
Runtime.enable, which asks Chrome to report execution contexts. That call is observable from inside the page.Runtime.enable makes the instant CAPTCHA disappear — even from the same residential IP that was blocked seconds earlier.Runtime.enable — the approach taken by rebrowser-patches and Patchright.CDP detection is a bot-detection technique that identifies whether a browser is being controlled by a DevTools client rather than a person. It does not check IP reputation or mouse movement. It checks for side effects that only appear when the Chrome DevTools Protocol is attached and issuing commands.
The Chrome DevTools Protocol is how Chrome exposes its internals — DOM, network, runtime, debugger — over a WebSocket. When you open DevTools in your own browser, Chrome speaks CDP to itself. Puppeteer and Playwright are, at their core, just external CDP clients. They connect to the same protocol and send the same commands a human developer's DevTools panel would.
That shared foundation is a strength and a liability. It gives automation full control of the browser. It also means anti-bot systems can look for the fingerprints that control leaves behind.
To evaluate document.querySelector(...) or any script, an automation library needs a handle on the page's execution context. Puppeteer and Playwright get it by sending Runtime.enable, which tells Chrome to emit Runtime.executionContextCreated events for every frame.
Here is the leak. When Runtime.enable is active, Chrome starts previewing JavaScript objects for the (imagined) DevTools console. Create an Error object and read its stack property through a getter, and that getter fires the moment CDP serializes the object for preview — something that never happens in a normal, un-automated page. A few lines of detection code can trip this and score the session as automation before your scraper has parsed a single element.
The technique became public knowledge around June 2024, documented in detail by the rebrowser team. The uncomfortable implication: teams that had spent months on stealth plugins were fixing the wrong layer the entire time. The JavaScript fingerprint was clean. The protocol underneath it was shouting.
A residential IP address changes where the request appears to come from. CDP detection does not care where you are. It fires on a signal generated inside the browser process itself, so a ₹200-per-GB residential proxy and a datacenter IP get flagged identically once Runtime.enable has run.
This is the counterintuitive money trap in scraping. Teams scale up proxy spend to beat blocks that proxies were never going to fix. The block was decided by the browser's own behavior, not its address.
| Layer | What it controls | Does it fix CDP detection? |
|---|---|---|
| Residential proxy | Source IP reputation | No |
Stealth plugin (puppeteer-extra-plugin-stealth) | JS fingerprint flags | No — leaves Runtime.enable intact |
| CDP-layer patch (rebrowser, Patchright) | How JS is executed | Yes — avoids or isolates Runtime.enable |
The only durable fix is at the protocol layer. rebrowser-patches keeps CDP but runs your code in an isolated execution context or via addBinding, so Runtime.enable is never the trigger. Patchright forks Playwright to avoid the command entirely. Both are open source, and both are the honest answer to a problem no amount of proxy rotation solves.
CDP detection moves the contest from the page to the plumbing. As anti-bot vendors add checks below the JavaScript layer, "stealth" stops meaning a clever navigator override and starts meaning a browser runtime whose control channel does not leak. That is a harder engineering problem, and it is why patched forks exist at all — the base tools were never built to hide.
This is the layer we build DLBrowser around: a runtime that reaches blocked pages because its control channel does not announce itself, not because it rotates through a bigger proxy pool. When the detection is structural, the fix has to be structural too.
If you run headless automation at any scale, audit which library commands you emit before you audit your proxies. The cheapest win in scraping right now is not a better IP — it is not calling Runtime.enable. For related teardown on the handshake layer, see our dispatch on TLS fingerprinting, and more field notes in our research.
What is CDP detection in web scraping?
CDP detection is an anti-bot technique that identifies when a browser is controlled by a Chrome DevTools Protocol client, such as Playwright or Puppeteer, rather than a human. It looks for side effects of automation commands like Runtime.enable instead of checking IP address or mouse behavior.
Why does Cloudflare block Playwright even with residential proxies?
Because Cloudflare's CDP detection reads a signal generated inside the browser, not the network. The Runtime.enable command that Playwright issues to run JavaScript is observable from the page. Changing your IP does nothing, since the automation gives itself away before any request content is inspected.
How do you fix the Runtime.enable leak?
Stop triggering it. Tools like rebrowser-patches run your JavaScript in an isolated execution context or through addBinding, so Runtime.enable is never the entry point. Patchright forks Playwright to avoid the command outright. Standard stealth plugins do not fix this — they only patch the JavaScript fingerprint.
When did the Runtime.enable CDP detection become public?
It became widely known around June 2024, when the rebrowser team published a detailed explanation. Anti-bot vendors including Cloudflare and DataDome had reportedly used the signal for years before it was documented openly.
Abhishek Gupta is Co-Founder at Dekrypt Labs, building DLBrowser — a stealth browser runtime for real-world data collection. dekryptlabs.com