A stealth browser is usually not a browser. It is ordinary Chrome plus a layer of patches that hide the traces automation leaves behind, and the only question that matters is which traces a given layer actually covers.
That boundary moved twice in the last three years, in opposite directions, and most of the tooling has not caught up with either move.
What counts as a stealth browser?
Three distinct products answer to the name, and they fail for different reasons.
- A patched automation library.
playwright-stealth,puppeteer-extra-plugin-stealth, rebrowser-patches, Camoufox. Chrome runs on your machine or in your container, and injected scripts overwrite the properties automation sets. - A hosted stealth browser. Browserless stealth routes, Hyperbrowser’s stealth mode, the stealth tiers sold by cloud browser vendors. Someone else runs the patched browser and you attach over CDP or an API.
- An antidetect browser. The multi-accounting tools, built for a human clicking rather than an agent scripting. Different buyer, different failure mode, covered separately in what an antidetect browser is.
The first two are what an engineer means when they search for a stealth browser. Both rest on the same premise: the browser is fine, and the automation on top of it is what gives you away.
| Approach | Where Chrome runs | Who maintains the patches | What it does not cover |
|---|---|---|---|
| Patched automation library | Your machine or container | You, plus whatever upstream still ships | The CDP layer, and the gap between the browser’s location and the exit IP |
| Hosted stealth browser | The vendor’s datacenter | The vendor | The same browser-to-exit-IP gap, since the proxy is still a separate machine |
| Antidetect browser | An operator’s desktop | The vendor | Scripted agent workloads; it is built around a human clicking |
| Real device (Archonum) | The smartphone that owns the IP | Nobody, because there is no patch layer | Nothing to patch, but it costs more per request than a container |
What does a stealth browser actually patch?
It patches JavaScript properties that a normal Chrome fills in and an automated one does not. Every one of them is read from inside the page.
Start with the signal every stealth guide opens on. navigator.webdriver is a read-only boolean the browser sets to true when it is under automation control, and per MDN’s reference, Chrome sets it when launched with --enable-automation, with --headless, or with a remote debugging port of 0.
It exists on purpose. The WebDriver specification wanted a standard way for a cooperating browser to tell a page it is being driven, so that sites could take a different code path.
Patching it takes one line, and it is the first thing every stealth layer does. The rest of the surface has the same shape. The 17 evasion modules in puppeteer-extra-plugin-stealth are each named after the property they repair, so reading the directory listing is the fastest way to see the scope:
- Chrome-only globals a headless build historically left empty:
chrome.app,chrome.csi,chrome.loadTimes,chrome.runtime. - Navigator fields that come back wrong or empty under automation:
navigator.plugins,navigator.languages,navigator.permissions,navigator.vendor,navigator.hardwareConcurrency,navigator.webdriver. - Rendering and media tells:
webgl.vendor,media.codecs,window.outerdimensions,iframe.contentWindow.
That list is the whole scope, which is where the ceiling comes from: a patch can only rewrite what the page can ask for.
The best-known patch set stopped moving in March 2023
The last commit on the default branch of berstend/puppeteer-extra is dated 1 March 2023. The repository carries 275 open issues against 7,390 stars, checked 5 August 2026.
Nothing about that is a criticism of the maintainer. It is a description of the risk you inherit when a patch set is the load-bearing part of your infrastructure, because the thing it patches ships every four weeks and the patches do not.
Chrome moved underneath it almost immediately. Chrome’s own documentation records that from Chrome 112 the browser unified headless and headful mode, so headless now shares code with regular Chrome instead of being, in Chrome’s phrasing, “a separate, alternate browser implementation.”
That change deleted a whole class of tells for free. The old headless binary was a different browser that got a lot of small things wrong, which is exactly why so many evasions are named after empty Chrome globals. Run new headless and several of those patches are repairing something that is no longer broken, while contributing their own detectable overwrites on the way past.
The leak that lived below JavaScript
While the JavaScript surface was getting less interesting, detection moved down a layer, to the protocol the automation speaks. No stealth plugin reaches it.
Playwright, Puppeteer and Selenium 4 all drive Chrome over the Chrome DevTools Protocol. Enabling CDP’s Runtime domain turns on reporting of execution contexts, and per the protocol reference the same domain emits consoleAPICalled and exceptionThrown once active.
To emit those events the browser serializes JavaScript objects out to the debugging client, and serializing an object means reading its properties, which runs any getter defined on them. Antoine Vastel published the resulting detection at Castle on 28 August 2025, in eight lines:
let wasAccessed = false;
const e = new Error();
Object.defineProperty(e, 'stack', {
get() {
wasAccessed = true;
return '';
}
});
console.debug(e);
If wasAccessed is true afterwards, something outside the page read that error object, and the only thing that does that is a debugging client attached over CDP.
No navigator patch touches this. The signal is not a property with a wrong value; it is a side effect of the browser being remotely controlled at all. That is why rebrowser-patches exists as a separate project working at a different level: rather than fixing the value, it avoids calling Runtime.enable in the first place, recovering execution context ids through Runtime.addBinding or an isolated world instead.
Then V8 closed it, and there was no announcement
Two V8 commits on 7 and 9 May 2025, titled “Avoid error side effects in DevTools” and “Apply getter guard throughout error preview,” stopped the engine running user code while previewing an error. DevTools now checks whether a getter is user-defined and skips it if so.
The detection above returns false today, on a browser that is unambiguously under CDP control. Vastel’s conclusion is the part worth carrying: a single decisive signal is not what serious detection runs on any more, and vendors that leaned on this one lost it without noticing.
For anyone running a stealth browser in production, both moves land in the same place. The signal you patched last year may be gone, the patch may now be the loudest thing you emit, and nothing in your stack tells you which. Blocking is the only symptom you get, it arrives weeks late, and it looks like a proxy problem.
Meanwhile the population you are hiding inside keeps growing. Cloudflare reported on 19 March 2025 that AI crawlers generate more than 50 billion requests a day across its network, just under 1% of all web requests it sees. Detection vendors are not short of examples to train on.
None of this is about defeating a security control. The workloads worth building are the ones where a real user could load the same page: public pricing, public listings, your own accounts, your own ad placements. If a stealth layer is what stands between your agent and someone’s login wall, the tooling is not the problem you have.
Which stealth browser setup fits which workload
A local Playwright with a stealth plugin costs nothing but the machine, and on sites with no bot vendor in front of them it works indefinitely. Pin your Chrome version, because each patch was written against a particular build and both keep moving. Budget the maintenance honestly: the reference patch set has not shipped a commit since March 2023, so the tracking is yours now.
What you are buying is the maintenance rather than the patches: a vendor whose job is to notice things like the May 2025 V8 change before your success rate does. It is still a patched browser in a datacenter, so the exit IP and the browser remain two different machines, and that seam is a separate signal from anything the stealth layer covers.
Archonum runs Chrome for Android on 250,000+ real consumer smartphones across 175+ countries, and the browser is on the handset that owns the IP, one network hop from the target. There is no patch layer to maintain because there is nothing to patch: the fingerprint is real because the device is real. It costs more per request than a container, and for an unprotected page that is money wasted.
Before adopting any of these, the test worth applying is not which one passes a fingerprint check today, but which one still works after the next Chrome release, and who is on the hook for finding out.
A stealth browser puts that on your maintenance budget. Archonum takes it off the table: agent traffic runs on real consumer smartphones, so there is no patch set to keep current and nothing synthetic underneath for a detector to contradict. Both are legitimate positions, and the cheaper one is right more often than vendors like us tend to admit.
