What Is a Headless Browser? Engines and Timings

A headless browser is a real browser engine with no window, driven by code. See how it works, launch and capture times for three engines, and code.

By Simon Chiu · Updated

A headless browser is a web browser that runs without a visible window and is controlled by a program, so it can load pages, run their JavaScript and produce screenshots, PDFs or page data with no person at the screen.

The engine inside is the same one desktop users run: Blink in Chromium, Gecko in Firefox, WebKit in Safari. Only the window is missing. That makes a headless browser the standard tool for screenshot services, end-to-end tests, crawlers that need rendered HTML, and AI agents that browse. This entry is part of the screenshot and rendering glossary.

A plain explanation

A normal browser has two jobs: compute the page, and show it to you. A headless browser does only the first job. It still downloads the HTML, applies the CSS, runs the scripts, lays out every element and paints pixels into memory. Your code then asks for the result: "give me a PNG of the first 800 pixels", "print this to PDF", "what text is in this element?".

How it works technically

Your scriptPuppeteer, Playwright CDP or BiDiPNG, PDF, DOM data Browser processno window Renderer processes parse HTML and CSSrun JavaScriptlayout, paint offscreen
The layers of a headless capture. The automation library never touches pixels itself; it sends protocol commands and receives the encoded result.

Your script talks to the browser over a debugging protocol. For Chromium that is the Chrome DevTools Protocol (CDP), a JSON message channel over a WebSocket or a pipe. WebDriver BiDi is the newer cross-browser standard, which Firefox and recent Chrome support. A command such as "capture a screenshot" makes the renderer paint the current frame into an offscreen buffer, encode it, and send it back as base64.

Because nothing is shown, a headless browser needs no display server and fits in a container or a serverless function. It still needs the same CPU, memory and fonts as a desktop browser, which is where most production trouble starts.

How fast is a headless capture?

We launched each of Playwright's three bundled engines, loaded a small local test page and took a 1280 x 800 screenshot, three runs each. The machine was an Apple silicon Mac that was running other jobs at the same time, so treat the numbers as relative, not absolute.

Engine (Playwright 1.64.1) Version Launch, median ms Load + capture, median ms PNG bytes
Chromium (headless shell) 156.0.8078.4 82 82 69,503
WebKit 27.2 202 369 86,959
Firefox 157.0 504 774 80,947

Two lessons came out of the run. First, the browser launch is a large share of a single capture, which is why services keep browsers warm and reuse them. Second, the three engines produced three different files from the same page, because each one rasterises text and anti-aliasing its own way. If you compare screenshots over time, pin one engine and version.

How to run one in Puppeteer, Playwright and RenderScreenshot

Puppeteer launches Chrome headless by default.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(process.argv[2] ?? 'https://example.com');
  console.log(await page.title(), '|', await page.evaluate(() => navigator.userAgent));
  await page.screenshot({ path: 'headless.png' });
} finally {
  await browser.close();
}

It printed Example Domain and a user agent that contains HeadlessChrome/148.0.7778.97, the token many sites use to spot automation.

Playwright drives three engines with one API. Each launch is headless unless you pass headless: false.

import { chromium, firefox, webkit } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
for (const engine of [chromium, firefox, webkit]) {
  const browser = await engine.launch(); // headless is the default
  try {
    const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
    await page.goto(url);
    await page.screenshot({ path: `${engine.name()}.png` });
    console.log(engine.name(), browser.version(), 'ok');
  } finally {
    await browser.close();
  }
}

RenderScreenshot runs the headless browser for you. We render with headless Chromium on Cloudflare's Browser Rendering platform, so a capture is one HTTP request and there is no browser process on your servers to launch, patch or restart.

curl --get https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" \
  --data-urlencode 'url=https://example.com' \
  -d width=1280 -d height=800 \
  --output page.png

Tested on 2026-10-15 with Node 22.18, Puppeteer 24.43.1 (run in headless shell mode) and Playwright 1.64.1. The Playwright loop was run against a local test page. The curl request follows the GET endpoint docs and was not run for this page.

Common mistakes

  • Launching a browser per request. Launch time dominates small captures. Reuse one browser and open a new context per job.
  • Forgetting browser.close(). Leaked browser processes are the classic cause of memory growth on screenshot servers. Close in finally.
  • Assuming a desktop look. Headless defaults can differ from your laptop: fonts, colour scheme, viewport. Set them explicitly.
  • Missing fonts on Linux. A slim container often lacks emoji and CJK fonts, so text renders as boxes.

For the visible differences between headless and windowed Chrome, see headless vs headful Chrome. For hosted options, see the headless browser API roundup. To skip running browsers yourself, create a free account.

Frequently asked questions

Is a headless browser different from a normal browser?
It uses the same engine for HTML, CSS, JavaScript, layout and paint. It does not open a window, so it has no visible tabs, toolbars or GPU-backed display. A few defaults differ, such as the user agent string, which pages can detect.
Which headless browser should I use for screenshots?
Headless Chromium for most work, because most visitors use Chromium-based browsers and the tooling is the most mature. Add Firefox or WebKit through Playwright when you need to see how those engines render.
Can websites detect a headless browser?
Some can. Signals include the HeadlessChrome user agent token and navigator.webdriver being true under automation. Bot-protection services use these and many more signals.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.