Problem
Playwright sets navigator.webdriver = true via CDP Runtime.enable. No Chrome flag can prevent this — it's baked into the driver.
The current workaround (addInitScript to override the property) is detectable: it creates an own-property on navigator that shouldn't exist (real browsers have it on Navigator.prototype only).
Proposal
Switch to Patchright — a drop-in Playwright fork that:
- Avoids
Runtime.enable entirely (executes JS in isolated contexts)
- Adds
--disable-blink-features=AutomationControlled
- Removes
--enable-automation from default args
- Patches Console.enable, command flag leaks, and other detection vectors
Passes Cloudflare, Akamai, DataDome, Fingerprint.com, CreepJS, etc.
Changes (~10 lines)
Dockerfile — replace base image + install patchright instead of playwright
pyproject.toml — playwright>=1.60.0 → patchright>=1.60.0
api.py / tests — from playwright.async_api → from patchright.async_api
Tradeoffs
- Console API disabled (use JS loggers if needed)
- Chromium-only patches (Firefox/WebKit unsupported — not relevant for us)
- Third-party dependency, but actively maintained and auto-deployed from upstream Playwright releases
Problem
Playwright sets
navigator.webdriver = truevia CDPRuntime.enable. No Chrome flag can prevent this — it's baked into the driver.The current workaround (
addInitScriptto override the property) is detectable: it creates an own-property onnavigatorthat shouldn't exist (real browsers have it onNavigator.prototypeonly).Proposal
Switch to Patchright — a drop-in Playwright fork that:
Runtime.enableentirely (executes JS in isolated contexts)--disable-blink-features=AutomationControlled--enable-automationfrom default argsPasses Cloudflare, Akamai, DataDome, Fingerprint.com, CreepJS, etc.
Changes (~10 lines)
Dockerfile — replace base image + install patchright instead of playwright
pyproject.toml —
playwright>=1.60.0→patchright>=1.60.0api.py / tests —
from playwright.async_api→from patchright.async_apiTradeoffs