Tools

Does playwright-stealth still beat bot detection?

Short version: playwright-stealth still slips past lazy bot checks, and on its own it won't get you through Cloudflare or DataDome in 2026. It hides automation tells by injecting JavaScript into the page, and the serious detectors either catch that patching or read signals your script can't reach.

Disclosure: some links here are affiliate links. If you sign up through them we may earn a commission at no extra cost to you. We only recommend tools we'd use ourselves. Hire a Clawd is our own service.

playwright-stealth does one job, and it does it entirely inside the page. Before your code navigates anywhere, it injects a bundle of JavaScript that rewrites the browser properties an automated Chromium leaks. It's the Python port of puppeteer-extra-plugin-stealth, the same evasion pack that's circulated the Node scraping world for years.

For a while that was enough. Flip navigator.webdriver to false, fake a couple of plugins, harmonize one permissions check, and most detection scripts waved you through. But the detectors moved, and injected JavaScript is exactly the approach they learned to catch. So the useful question in 2026 isn't whether playwright-stealth works. It's against what.

What playwright-stealth actually patches

Everything it does happens in page-script land, before your navigation fires. The evasions it ships are a subset of the puppeteer-extra pack, ported to Python. The ones that carry the weight:

Run all of that and your browser looks a lot more like a person's than a fresh Playwright launch does. On a page that only checks navigator.webdriver, you're invisible. The trouble starts when the page checks how you became invisible.

Installing and running it

One pip install, plus the browser binary if you don't have it yet:

pip install playwright-stealth
playwright install chromium

The current package exposes a Stealth class. The cleanest way to use it wraps Playwright's own context manager, so every page and context you open gets patched automatically and you can't forget to apply it:

from playwright.sync_api import sync_playwright
from playwright_stealth import Stealth

with Stealth().use_sync(sync_playwright()) as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://bot.sannysoft.com/")
    page.screenshot(path="check.png", full_page=True)
    browser.close()

Async is the same shape with use_async:

from playwright.async_api import async_playwright
from playwright_stealth import Stealth

async def main():
    async with Stealth().use_async(async_playwright()) as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://bot.sannysoft.com/")
        await browser.close()

If you're following an older tutorial you'll see from playwright_stealth import stealth_sync and a bare stealth_sync(page) call right after you create the page. That's the pre-1.0 API. The stealth_sync and stealth_async helpers still exist, but the Stealth class is what the maintained package points you at now, and it's harder to misuse.

Point any of these at bot.sannysoft.com and you'll watch the red rows flip green. That test is the catch, though. It sits at the naive end of the spectrum, and passing it tells you almost nothing about how you'll fare against a target that's actually paying a vendor to keep you out.

Where it stops working

Here's the uncomfortable part. The thing that makes playwright-stealth convenient, that it patches from inside the page, is also its ceiling.

The patches are detectable as patches. Override a native browser function from JavaScript and you leave marks. The function's toString() can stop reading [native code]. Property descriptors look subtly off. Stealth tries to paper over those too, but now you're patching the patches, and fingerprinting scripts like CreepJS are built to hunt for exactly that recursion. Worse, the spoofed values are known constants. A detector that's seen playwright-stealth before can match its specific fake WebGL strings and flag them on sight. You're not invisible. You're wearing a costume the bouncer has seen a hundred times.

It can't touch anything below the page. An init script runs in the page. That's the whole sandbox. It can't reshape your TLS ClientHello, it can't reorder your HTTP/2 frames, and it can't scrub the Chrome DevTools Protocol channel Playwright drives the browser over. There's good news buried in that: because Playwright launches real Chromium, your raw TLS and HTTP/2 fingerprints already look like Chrome, which is more than a plain requests script can say. The bad news is the CDP layer. Automating Chromium over DevTools leaves artifacts, like the Runtime.Enable call that fires on startup, and no page script can reach down and erase them. Cloudflare and DataDome probe that layer directly.

It does nothing about your IP. Nothing at all. Run from a cloud box and the target sees a datacenter IP and can be done with you before a single line of your stealth JavaScript executes. This is the layer people skip, then blame the browser.

It does nothing about behavior. No cursor movement, instant page-to-page jumps, requests spaced to the millisecond. A flawless fingerprint that acts like a machine still reads as one.

Stack those up and you get the honest 2026 answer on hard targets. Against Cloudflare's managed challenge or DataDome, playwright-stealth on its own mostly loops or eats a 403. Not because the patches are bad, but because the detector is reading a whole stack of signals and your script only reaches one floor of the building.

Recommended tool

Stealth fixes the browser, not the IP

playwright-stealth can make your fingerprint pristine and still get you blocked on the IP alone, because no injected script touches the network layer. A rotating residential pool with clean, non-flagged IPs covers the part stealth can't, and it's the single change that unblocks the most scrapes. We break down how to choose one in the residential proxy guide.

See our proxy pick →

Affiliate link, at no extra cost to you.

When it's still the right call

None of that makes playwright-stealth useless. It makes it situational. Reach for it when:

The rule I'd give anyone: match the tool to the defense. playwright-stealth is a fine first move on soft targets and a wasted afternoon against Cloudflare. If you can't tell which you're facing, read the block first before you escalate.

Matching the tool to the target, every time

Reading the defense, picking the stealth layer, stacking proxies and human timing on top, then redoing it when the site changes its scoring is real, recurring work. Hire a Clawd is a personal AI agent that runs the whole browser-automation stack for you, 24/7, so you get the data without babysitting the machine that fetches it.

See plans from $49/mo →

The stronger play: spoof at the source

If injected patches are the weakness, the fix is obvious in hindsight. Don't inject. Fake the values so deep in the browser that they read as native, because at that point they are.

That's Camoufox. It's a custom build of Firefox where the fingerprint spoofing is compiled into the browser's C++ internals instead of bolted on as JavaScript. When a detector reads navigator, WebGL, canvas, or fonts, there's no evasion script sitting in the page to catch, because the value was faked down in the engine. Nothing to toString(), no descriptor to inspect, no known-constant costume to match. The lie is native all the way down.

It talks to your code through Playwright, sync or async, so the example you just wrote for playwright-stealth mostly carries over. A few constructor options do the heavy lifting: os picks the fingerprint's operating system, humanize gives you curved, human-shaped cursor paths, and geoip=True lines timezone and locale up with your proxy's exit IP.

It's not magic, and I won't pretend otherwise. Camoufox is pre-1.0, it's Firefox (so a handful of Chromium-only sites act weird), and a real browser is heavier on RAM than a bare request. But on a target that actually fights back, engine-level spoofing holds up where injected patches fall over. That's the entire reason to switch.

Recommended tool

Spoofing at the source, without the build

Camoufox is the DIY route to native-level spoofing. A managed anti-detect browser is the same idea, packaged: engine-level fingerprints, isolated per profile, no compile step and no version to pin and babysit. If you'd rather point and click than build and maintain, this is the shortest path to the same place.

See our anti-detect browser pick →

Affiliate link, at no extra cost to you.

The Chrome-side sibling

If you're wedded to Chrome instead of Firefox, playwright-stealth's closest cousin is undetected-chromedriver. Same core instinct, opposite engine. It patches a real Chrome to strip the automation tells rather than injecting them away inside the page, and it rides Selenium instead of Playwright, so it's the drop-in when your codebase is already Selenium.

It carries the same structural problem, plus a maintenance one. It's still Chrome over CDP, so the patch-and-detect arms race applies, and its last stable release is old enough that serious targets catch it more often than they used to. I put the two side by side in Camoufox vs undetected-chromedriver if you're choosing between engines.

The pattern across all three is the same. playwright-stealth, undetected-chromedriver, and the rest of the injected-JS crowd patch a browser the detectors already know by heart. Camoufox rebuilds the browser so there's nothing to patch. On easy targets the difference is invisible. On hard ones it's the whole game.

Skip the setup

The Anti-Detect Scraping Starter Kit

A ready-to-run Camoufox and residential-proxy Python template with humanized behavior baked in, a setup guide, and a pre-flight checklist that walks every layer this post just covered. The stronger half of the stealth story, wired up and working, so your first real scrape doesn't get you insta-blocked.

$19 one-time

Get the kit →

FAQ

Is playwright-stealth detectable?

Yes, and more so every year. The patches it injects leave their own marks: altered toString() output, off-looking property descriptors, and known-constant spoof values that fingerprinting scripts recognize on sight. It'll pass a naive check and fail a fingerprinting-grade one like CreepJS or a tuned Cloudflare rule.

Does playwright-stealth work against Cloudflare?

On its own, usually not in 2026, at least not against Cloudflare's managed challenge. It patches page-level tells, but Cloudflare also weighs IP reputation and the CDP automation layer, neither of which an injected script can reach. Add residential proxies and you improve your odds on lighter Cloudflare setups. Against a hardened one you're better off with a browser that spoofs at the engine level. More in the Cloudflare bypass guide.

playwright-stealth vs undetected-chromedriver?

Same philosophy, different engine. playwright-stealth injects evasions into Playwright-driven Chromium; undetected-chromedriver patches a Selenium-driven Chrome. Both dress up a browser the detectors already know, and both get caught on serious targets. Pick by your existing stack, then read the full comparison for the details.

Is there a better alternative in 2026?

For hard targets, yes. Camoufox spoofs at the browser's C++ level, so the faked values read as native and there's no injected patch to detect. It's the anti-detect browser most scrapers graduate to once playwright-stealth stops clearing their target. Whatever browser you run, a protected site still needs residential proxies for the IP layer, and the nastiest ones may want the DataDome playbook on top.

Or skip the whole escalation ladder

Picking the right stealth tool, keeping it current, stacking proxies and behavior on top, and re-checking all of it every time a detector updates is a standing job. Hire a Clawd runs the automation for you, around the clock, picks the right stack per target, and pings you on Telegram or Signal when the data's ready. No scripts to babysit.

Get your own agent →