Guide

Playwright stealth in 2026: what it patches, and where it stops working

playwright-extra and the stealth plugin fix the JavaScript-visible layer. That layer stopped being the hard one. Here is what they still cover, what they cannot, and how to tell which problem you have.

Playwright is the best browser automation tool available, and nothing else is close. It is also, out of the box, one of the most reliably detected things you can point at a defended endpoint. Both are true for the same reason: Playwright is built for testing your own application, where nothing is trying to identify it.

The stealth packages grew up to close that gap. Understanding what they do is the fastest way to work out whether they will solve your problem.

What the stealth plugin patches

playwright-extra wraps a Playwright browser type and lets you attach plugins. The canonical one is a port of puppeteer-extra-plugin-stealth, and it works by overriding properties in the page before any site script runs.

Concretely, it addresses things like:

  • navigator.webdriver, which Playwright sets to true by default
  • navigator.plugins and navigator.mimeTypes, which are empty in automation but populated in a real browser
  • navigator.languages and navigator.platform, which often disagree with the User-Agent
  • The chrome.runtime object, absent when a page expects Chrome
  • WebGL vendor and renderer strings, which reveal SwiftShader or a headless renderer
  • Permission query behaviour, which differs between automation and a real browser
  • A collection of smaller inconsistencies: Notification.permission, iframe contentWindow, toString leaking native code markers
import { chromium } from "playwright-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";

chromium.use(StealthPlugin());

const browser = await chromium.launch({ headless: true });

That is the whole setup. It fixes a real category of detection, and against older or JavaScript-only defences it is often sufficient.

Where it stops

The problem is that the properties above are all reachable from JavaScript, and the defences that matter most today are not.

TLS fingerprints. The handshake that opens the connection has a signature: ciphers, extensions, and their ordering, hashed as JA3 or JA4. This happens below the JavaScript layer entirely. By the time navigator.webdriver is being patched, the server has already formed an opinion. Playwright's Chromium can be made to match a real Chrome build, but the default configuration does not, and the plugin has no involvement in it either way.

HTTP/2 characteristics. Frame settings, window updates, header order, and priority frames form a second signature. Also below JavaScript.

IP reputation. Already scored from the ASN. Nothing in the page context affects it.

CDP detection. Playwright drives the browser over the Chrome DevTools Protocol. The protocol connection leaves artefacts that can be probed from page script. Detection here is an active area, and it does not care what properties you have overwritten.

Behavioural scoring. How the session moves: timing, cadence, interaction. A patched browser that behaves like a script still scores like a script.

The practical consequence: teams install the stealth plugin, see a target start working, then find that a different target still fails, and conclude the plugin is broken. It is not broken. It solved one of five problems, and the target they are testing against scores on a different one.

Working out which problem you have

Do this before changing any code, because the fix differs and guessing wastes days.

Test from a residential connection. Run the same script on a home connection instead of your cloud host. If it passes, the block is IP reputation and the fix is a proxy tier. If it fails identically, the IP was never the issue.

Test with a real browser. Fetch the same URL in Chrome by hand from the same machine. If that works and the script does not, something in the automation stack is detectable, and it may be CDP or TLS rather than a navigator property.

Compare the failure modes. A 403 with a challenge body is bot management. A 200 with an empty body is usually a challenge page whose JavaScript did not complete. An intermittent failure that correlates with volume is rate limiting, not detection. These need different responses and are frequently mistaken for each other.

Check whether you are being rate limited. Detection and throttling look similar from a failing script. If adding a delay fixes it, you never had a fingerprinting problem.

What moves the needle

In rough order of impact per unit of effort:

  1. Realistic pacing. Most "detection" problems at low volume are self-inflicted. Slow down, cache aggressively, and re-measure. This costs nothing.
  2. Persistent browser contexts. A session that keeps cookies, storage, and a stable identity across requests looks far more like a returning user than a fresh profile every time. Playwright's launchPersistentContext gives you this.
  3. Residential or static ISP routing. Addresses IP reputation directly. Static ISP typically outperforms rotating residential for session-based access.
  4. Stealth plugin. Cheap, worth having, fixes a real but narrower class of checks.
  5. A browser build whose TLS and HTTP/2 fingerprints match a shipping Chrome. This is the step that most self-managed stacks never complete, because it means patching or building the browser rather than configuring it.

Steps 1 through 4 are a day of work and cover a useful share of targets. Step 5 is where a self-managed stack turns into a system that needs someone watching it.

When to stop maintaining it

The question is not whether you can get past a given target. With enough effort you usually can. The question is what it costs to keep working next quarter, when the fingerprints shift and the challenge vendors update.

If page retrieval is incidental to your product, that maintenance is pure overhead against a roadmap that has nothing to do with it. A managed extraction API that returns validated data is trading a known per-page cost for an unknown and recurring engineering cost. For a lot of teams that trade is obviously correct, and the interesting question is why they put it off.

If retrieval is the product, owning the transport is defensible and probably necessary, because nobody sells you the exact thing you sell.

Whatever route you take, this is about reaching publicly accessible pages. Respect robots.txt, keep request rates courteous, and do not point any of this at authentication walls. See our acceptable use policy.

datamint returns schema-validated JSON and LLM-ready Markdown from bot-defended sites through one API call, with routing across transport tiers handled on our side.

All guides