Skip to content

What Is a Headless Browser? Uses, Tools and Detection

A headless browser is a real browser without a visible window, driven from code. Headless vs headed, how Chrome's headless mode works today, tested Playwright, Puppeteer and Selenium examples, how sites detect headless browsers, and when you need one.

Published
Reading time
7 min read
Pixel-art robot with an orange >_ on its terminal head pulls a data card out of a browser page whose title bar has been lifted off and floats above it, a dotted line where it sat, and drops it into a crate
In this article
  1. Headless vs headed browsers
  2. How Chrome's headless mode works today
  3. Headless browser examples: Playwright, Puppeteer and Selenium
  4. Playwright (Python)
  5. Puppeteer (Node.js)
  6. Selenium (Python)
  7. What is a headless browser used for?
  8. How websites detect headless browsers
  9. When to use a headless browser (and when not to)
  10. Tips for scraping with a headless browser
  11. Headless browsers on Scrape.ly
  12. FAQ
  13. What is headless mode?
  14. Is a headless browser faster?
  15. Can websites detect a headless browser?
  16. What is headless Chrome?
  17. Is PhantomJS still used?
  18. Do I need a headless browser for web scraping?

What is a headless browser? It's a web browser that runs without a visible window. It loads pages, runs JavaScript, sets cookies and builds the page exactly like Chrome or Firefox on your desktop, but you control it from code or the command line instead of a screen.

That makes it the standard tool for anything that needs a real browser without a person in front of it: scraping pages that render with JavaScript, automated testing, screenshots and PDFs, and AI agents that browse. Below: headless vs headed, how Chrome's headless mode works today, working examples in Playwright, Puppeteer and Selenium that we tested on 26 October 2026, how websites detect headless browsers, and when you need one.

Headless vs headed browsers

Headless browser Headed browser
Window None Visible window and tabs
Controlled by Code (Playwright, Puppeteer, Selenium) or command-line flags A person, or code you can watch
JavaScript, cookies, DOM Full support Full support
Resource use Lower: nothing is drawn on screen Higher
Runs on servers without a display Yes Needs a virtual display
Debugging Logs, screenshots, traces You watch it happen
Detectable as automation Yes, by default Less, but automation flags still show

The engine is the same in both columns. Headless only removes the window. Chrome's documentation puts it neatly: with headless mode, "you can run Chrome without chrome", the second "chrome" meaning the browser's interface (Chrome Headless mode).

How Chrome's headless mode works today

For years, "headless Chrome" was a separate, stripped-down browser that happened to ship inside the Chrome binary. Google's docs describe it as "a separate, alternate browser implementation" that "didn't share any of the Chrome browser code". In Chrome 112 that changed: the new headless mode is the real Chrome, which "creates, but doesn't display, any platform windows". Since Chrome 132, the old implementation only ships as a separate binary called chrome-headless-shell.

Which one you get depends on your tool:

  • Puppeteer uses the new headless mode with headless: true (the default) and the old one with headless: 'shell'.
  • Playwright uses the Chromium headless shell by default when running headless; its docs say you "can opt into the new headless mode by using 'chromium' channel" (Playwright browsers).
  • Selenium passes Chrome's own flag: --headless=new or simply --headless on current versions.

The difference matters for scraping, because the shell is easier to detect. More on that below.

You don't even need a library for simple jobs. Chrome's command line can print a page's rendered HTML or save a screenshot:

shell
google-chrome --headless --dump-dom https://books.toscrape.com/ > books.html
google-chrome --headless --screenshot=books.png --window-size=1280,800 https://books.toscrape.com/

On macOS, call the binary inside Google Chrome.app/Contents/MacOS/. With Chrome 155 both commands worked in our test: the dumped DOM contained every book title, and the screenshot was the full first screen of the page.

Headless browser examples: Playwright, Puppeteer and Selenium

Each example opens books.toscrape.com, a sandbox site made for scraping practice, and prints the first five titles and prices. All three ran on 26 October 2026 on macOS.

Playwright (Python)

Install with pip install playwright, then playwright install chromium:

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://books.toscrape.com/")
    for card in page.locator("article.product_pod").all()[:5]:
        title = card.locator("h3 a").get_attribute("title")
        price = card.locator(".price_color").inner_text()
        print(title, price)
    print(page.evaluate("() => [navigator.webdriver, navigator.userAgent]"))
    browser.close()

With Playwright 1.64.1 it printed:

Text
A Light in the Attic £51.77
Tipping the Velvet £53.74
Soumission £50.10
Sharp Objects £47.82
Sapiens: A Brief History of Humankind £54.23
[True, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/156.0.8078.4 Safari/537.36']

The last line is the interesting one. We'll come back to it.

Puppeteer (Node.js)

Install with npm install puppeteer, which downloads a matching Chrome for Testing:

javascript
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://books.toscrape.com/', { waitUntil: 'domcontentloaded' });

const books = await page.$$eval('article.product_pod', (cards) =>
  cards.slice(0, 5).map((card) => ({
    title: card.querySelector('h3 a').getAttribute('title'),
    price: card.querySelector('.price_color').textContent,
  })),
);
console.log(books);
await browser.close();

With Puppeteer 25.16.0 and Chrome 156 it printed the same five books, from { title: 'A Light in the Attic', price: '£51.77' } to Sapiens.

Selenium (Python)

Install with pip install selenium. Selenium Manager finds or downloads the browser driver for you:

python
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
driver.get("https://books.toscrape.com/")
for card in driver.find_elements(By.CSS_SELECTOR, "article.product_pod")[:5]:
    title = card.find_element(By.CSS_SELECTOR, "h3 a").get_attribute("title")
    price = card.find_element(By.CSS_SELECTOR, ".price_color").text
    print(title, price)
print(driver.execute_script("return [navigator.webdriver, navigator.userAgent]"))
driver.quit()

With Selenium 4.51.0 driving the installed Chrome 155, it printed the same five books and [True, '... HeadlessChrome/155.0.0.0 Safari/537.36']. If you're choosing between the libraries, see Playwright vs Selenium.

What is a headless browser used for?

  • Web scraping. Pages that load their data with JavaScript, need scrolling or clicking, or render prices client-side. A headless browser returns the DOM after scripts run.
  • Automated testing. End-to-end tests in CI, where there's no screen and speed matters.
  • Screenshots and PDFs. Rendering pages into images or documents on a server.
  • Monitoring. Checking that a checkout, a login page or a price looks right, on a schedule.
  • AI agents. Agents that browse need a browser they can drive; a headless one, often in the cloud, is the usual choice.

How websites detect headless browsers

A headless browser is a real browser, but by default it doesn't hide that it's automated. All three examples above reported navigator.webdriver as true, a flag the WebDriver standard sets for automated browsers, and "HeadlessChrome" in the User-Agent. Any page script can read both.

We loaded bot.sannysoft.com, a public fingerprint test page, with Puppeteer 25.16.0 and Chrome 156 in both headless modes:

Check New headless (headless: true) Headless shell (headless: 'shell')
navigator.webdriver true true
"HeadlessChrome" in User-Agent Yes Yes
navigator.plugins.length 5 0
window.chrome present missing
Accept-Language header Sent Not sent
bot.sannysoft.com rows failed or warned 4 of 31 13 of 31

The new headless mode is far closer to a normal Chrome: it only failed the User-Agent, WebDriver, "HEADCHR_UA" and memory checks. The shell failed 13, including the missing Chrome object, the empty plugin list, the permissions check and the WebGL renderer check. Playwright's default headless mode uses the same shell, and our Playwright stealth test counted the same 13 flagged rows with it.

Beyond the JavaScript surface, sites look at signals no headless setting changes: the IP address (datacenter ranges get more scrutiny), the TLS handshake, and behavior such as request rate and navigation patterns. Stealth plugins patch the JavaScript tells; our tests of Puppeteer stealth and Playwright stealth show what they fix and what they don't. For the full picture, see web scraping without getting blocked.

When to use a headless browser (and when not to)

Use one when:

  • The data only appears after JavaScript runs, and you can't find the API call the page makes.
  • You need to interact: scroll, click "load more", fill a search form on a public page.
  • You need a screenshot or a PDF of what a visitor sees.

Skip it when:

  • The HTML already contains the data. An HTTP request with a parser skips starting a browser, loading images and running scripts, so it's far lighter and easier to scale.
  • The page loads its data from a JSON endpoint you can call directly.
  • You're scraping thousands of simple pages, where browser startup time adds up.

Tips for scraping with a headless browser

  • Wait for elements, not for time. page.wait_for_selector() in Playwright or page.waitForSelector() in Puppeteer returns as soon as the data is there; fixed sleeps are either too short or wasted.
  • Block what you don't need. Images, fonts and media make up much of a page's weight. Playwright's page.route() and Puppeteer's request interception let you abort them, which cuts both time and proxy traffic.
  • Reuse the browser, not the page. Starting a browser is the slow part. Open one browser, create a fresh context per job and close pages as soon as you're done.
  • Set a locale and time zone that match your IP. A browser that says en-US and Europe/Berlin while exiting from a US proxy is easy to flag.
  • Watch memory. Long-running headless sessions leak. Restart the browser every few hundred pages.

A good pattern is HTTP first, browser on demand. That's how the Web Scraper on Scrape.ly works: it fetches pages over HTTP while impersonating Chrome's TLS fingerprint, and launches headless Chromium only when you turn on useChrome. The Website Content Crawler does the same automatically, opening Chrome only when a page needs JavaScript.

Headless browsers on Scrape.ly

On Scrape.ly you can run headless browsers three ways, from least to most work.

Use a ready-made Actor. The Scrape.ly Store has 570+ Actors, and the ones for JavaScript-heavy sites run browsers for you. The Web Scraper above costs $0.71 per 1,000 results, platform usage (compute, proxy, browser) included.

Write your own Actor. Your Playwright or Puppeteer code runs as an Actor on Scrape.ly's cloud. The Crawlee + Playwright template gives you a working JavaScript project to start from; for Python, add the Actor files to your own project with scrapely init (see the Python deploy guide).

Run it on the Scrape.ly browser. For sites that block headless browsers, turn on the Scrape.ly browser for your Actor and rebuild. Every browser launch then connects to a hosted Chromium with anti-bot fingerprints, its own residential proxy and Cloudflare Turnstile solving, with no code changes. It costs $0.45 per browser hour at list price plus its traffic. Our guide to what a cloud browser is explains how hosted browsers work, and the anti-blocking page shows how the browser and the managed proxy fit together.

FAQ

What is headless mode?

Headless mode is a way of running a browser without its window. In Chrome you start it with the --headless flag; in Playwright, Puppeteer and Selenium you set an option, such as headless=True or --headless=new. The browser works normally; nothing is drawn on screen.

Is a headless browser faster?

Somewhat. It skips drawing the page on screen, so it uses less CPU and memory than a headed browser. It still downloads and runs everything a page needs, so it's much slower than a plain HTTP request.

Can websites detect a headless browser?

Yes, by default. In our tests, Playwright, Puppeteer and Selenium all reported navigator.webdriver as true and "HeadlessChrome" in the User-Agent. Sites also look at your IP, TLS fingerprint and behavior, which no headless setting changes.

What is headless Chrome?

Chrome running in headless mode. Since Chrome 112, it's the full Chrome browser without a visible window; the older, separate implementation now ships as chrome-headless-shell.

Is PhantomJS still used?

Rarely. PhantomJS, an early headless browser built on WebKit, is no longer developed: its GitHub repository is archived. Headless Chrome, Firefox and WebKit, driven by Playwright, Puppeteer or Selenium, replaced it.

Do I need a headless browser for web scraping?

Only for pages that need JavaScript or interaction. Many sites send their data in the HTML, and a simple HTTP request is faster and cheaper there. Start with HTTP, check whether the data is in the response, and switch to a browser for the pages where it isn't.

  • headless browser
  • headless chrome
  • playwright
  • puppeteer
  • selenium

About the author

The team behind Scrape.ly

Scrape.ly is the web data platform from TeraShift GmbH in Switzerland, powered by Evomi: ready-made scrapers in the Scrape.ly Store, a cloud runtime, an API with JavaScript and Python SDKs, proxies, a hosted anti-bot browser and an MCP server for AI agents.

//Keep reading\\

The internet is full of data.
Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes.

No credit card. No setup calls.