How to detect headless Chrome and automation
Headless browsers (Puppeteer, Playwright, Selenium) try to look like real Chrome, but they leave tells across several layers. Relying on any single signal is fragile; combining them is what works.
JavaScript / DOM tells
navigator.webdriver === true— the standard automation flag.- Chrome DevTools Protocol (CDP) artifacts and runtime hooks.
- Missing
window.chrome, emptynavigator.plugins/languages, SwiftShader/“Google Inc.” GPU under software rendering. - Native function tampering (overridden
toStringon patched APIs used by stealth plugins).
Network tells (hard to fake)
A headless run on a server has a TLS/JA4 and TCP/JA4T stack that often does not match the browser it claims to be — especially when proxied through a datacenter ASN. These live below the JavaScript sandbox, so stealth plugins cannot patch them.
Timing & behavior
Headless and VM environments show characteristic performance.now() resolution, requestAnimationFrame cadence and event-loop lag. Real users also generate mouse-curvature and keystroke-timing entropy that scripted input lacks.