Headless browsers: what they are and how websites recognize them
A headless browser is a normal browser engine running without a window. A script tells it which pages to open, what to type and where to click. Testers, monitoring services and scrapers use it every day, and so do bots that register fake accounts or try stolen passwords. A website cannot ask the visitor which of them it is, so it looks at what the browser reports and how the connection behaves.
Check your browser right now
Checking your browser…
Who runs headless browsers
Chrome, Firefox and WebKit can all run without a window. The usual tools that drive them are Puppeteer, Playwright and Selenium.
Most of this traffic is harmless: tests of a site before a release, uptime and speed monitors, services that turn pages into screenshots or PDF files. The same tools also power password guessing with stolen logins, mass sign-ups, promo code abuse and aggressive price scraping. Blocking every headless browser would also block your own tests and monitoring.
Signs in the browser itself
A browser under automation is supposed to say so. The WebDriver standard requires navigator.webdriver to be true, and Puppeteer, Playwright and Selenium leave it on by default. Headless Chrome also writes "HeadlessChrome" into its User-Agent unless the script changes it. These two signs catch honest tools and careless bots. A bot built to abuse a site removes both.
Other traces come from the environment the browser runs in:
- A software graphics renderer such as SwiftShader or llvmpipe instead of a real graphics card, typical for servers without a GPU.
- A visible page whose browser window has an outer size of zero.
- In desktop Chrome, a missing window.chrome object.
- An Android User-Agent with no motion sensor readings at all, which is how mobile emulation on a computer looks.
Each of these has an innocent explanation on some real computers, so one trace should lower a score, and blocking needs more than that.
Signs of a hidden headless browser
When a bot hides the obvious signs, it has to change values in several places at once, and some places get missed. A Web Worker or an iframe may still report the original platform or language. The User-Agent in JavaScript may differ from the one in the HTTP header or from Client Hints. Browser functions replaced by a script can look different from the built-in ones. Checks that read the same fact from two sources find these gaps.
Signs on the network
The server sees what a page script cannot change. Bots mostly run in data centers, so their IP address often belongs to a hosting provider or a VPN. The TLS handshake shows which software opened the connection: a script that sends requests through an HTTP library has a different TLS fingerprint (JA3, JA4) from the Chrome it claims to be. Behavior adds the rest: pages opened faster than anyone reads them, forms filled in a fraction of a second, many accounts from one device.
What SpoofLens checks
The check on this page looks for navigator.webdriver, HeadlessChrome in the User-Agent (in JavaScript and in the HTTP header), a zero-size window, a missing window.chrome in desktop Chrome, software rendering, replaced browser functions, a Web Worker or iframe that disagrees with the page, a TLS handshake from a non-browser library, a data center IP and, on Android, missing motion sensors. If it finds navigator.webdriver or HeadlessChrome, the verdict is "automation". Every finding comes with an explanation and lowers the truth index by its weight.
For website owners
Decide which actions need protection: sign-up, login, checkout, promo codes, contact forms. Let headless traffic read public pages, and add friction to these actions when the score is low, for example an email confirmation, a captcha or a manual review. Put your own monitors and test runners on an allowlist by IP address or by a secret header so they never get blocked.
The SpoofLens API returns the score, the verdict and the reasons for each sign-up or order. The WordPress / WooCommerce plugin shows them next to users and orders and blocks automated browsers only if you turn that on.
Frequently asked questions
- Is every headless browser a bot?
- No. Monitoring, testing, screenshot and PDF services use headless browsers all the time. Treat "headless" as one signal and look at what the visitor is trying to do.
- Can a headless browser pass the check?
- A carefully configured one passes some checks. Every value it changes has to stay consistent everywhere else, so contradictions remain the most reliable sign. Server-side signals such as the TLS handshake and the IP network are hard to fake from inside the browser.
- Will my uptime monitor be blocked?
- Only if you turn on blocking of automated browsers and leave the monitor off your allowlist. The API never blocks by itself, and in the plugin blocking is off by default.