ScreenshotNeo

BlogComparisons

Headed vs. Headless Browser Mode: Key Differences Explained

Headed mode shows a browser window; headless runs without one. Learn how Chrome, Playwright and Puppeteer differ, and which mode to choose.

By the ScreenshotNeo team1 October 20267 min read

Headed mode displays a browser window and its user interface. Headless mode runs without a visible UI. Both can render pages, execute JavaScript, submit forms, intercept network traffic, take screenshots and generate PDFs. The practical choice depends on where the browser runs, whether someone needs to watch it, and which browser build your framework selects.

Current Chrome Headless uses the same browser implementation as headed Chrome. However, automation frameworks can still select a separate legacy headless shell, so a headed run and a headless run are not automatically equivalent. Always check the framework, browser channel and version.

What headed and headless mean

Aspect Headed Headless
Visible window Yes, with normal browser UI No visible UI
Typical environment Developer workstation or interactive debugging Servers, containers and CI/CD jobs
Automation capability Can render, click, submit forms, intercept requests and capture output The same capabilities are available
Observation You can watch the session live Use logs, traces, screenshots or video to inspect it
Implementation Regular Chrome/Chromium build May be regular unified Headless or a legacy headless shell, depending on tooling

Chrome describes modern Headless and headful modes as unified: they share the browser implementation. The old Headless implementation became the standalone chrome-headless-shell binary starting with Chrome 132.0.6793.0. Puppeteer and Playwright both document differences between their regular browser builds and the shell.

When to choose each mode

Choose headless for unattended work

  • CI pipelines and scheduled jobs
  • Containers and server environments without a display
  • Large batches of screenshots, PDFs or page checks
  • Background form submission and monitoring

Choose headed for observation and diagnosis

  • You are developing a new automation flow
  • You need to inspect a consent dialog, popup or login sequence visually
  • A failure depends on focus, animation or viewport behavior
  • You want to reproduce what a local user sees

A common workflow is to develop headed, collect a trace or screenshot, then run headless in CI. Keep the browser channel and version consistent between those environments when visual fidelity matters.

Chrome command line examples

Headless screenshot

google-chrome \
  --headless \
  --disable-gpu \
  --window-size=1440,900 \
  --screenshot=page.png \
  https://example.com

Headed launch

google-chrome \
  --window-size=1440,900 \
  https://example.com

The --headless flag suppresses the visible UI. The exact executable name varies by operating system and installation. In containers, install a compatible Chrome/Chromium build and provide the sandbox configuration required by your runtime rather than copying flags blindly.

Puppeteer: headed, modern headless and shell

Puppeteer launches Headless by default. Set headless: false for a visible browser, or headless: 'shell' to request the legacy Chrome Headless Shell when your installed Puppeteer version supports it.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  // false = headed, true = regular headless, 'shell' = legacy shell
  headless: true,
  defaultViewport: { width: 1440, height: 900 }
});

const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'example.png', fullPage: true });
await browser.close();

For a local debugging run, change headless to false. If a result differs between true and 'shell', compare browser version, executable path, viewport, device scale factor, fonts and command-line arguments before changing application code.

Playwright: headless defaults and channels

Playwright’s BrowserType API defaults headless to true. Its default Chromium headless operation can use a headless shell. Selecting the chromium channel opts into the newer Headless implementation documented by Playwright.

import { chromium } from 'playwright';

const browser = await chromium.launch({
  headless: true,
  // channel: 'chromium' opts into the newer Chrome Headless path
});

const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'example.png', fullPage: true });
await browser.close();

To see the page while developing, use headless: false. To make a headed and headless comparison meaningful, use the same channel, browser version, viewport, locale, timezone, permissions and installed fonts.

Python Playwright example

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page(viewport={"width": 1440, "height": 900})
    page.goto("https://example.com", wait_until="networkidle")
    page.screenshot(path="example.png", full_page=True)
    browser.close()

Use headless=False while diagnosing a flow. Install the browser binaries required by your Playwright release, and pin the dependency in CI so an automatic browser update does not change rendering unexpectedly.

Why headed and headless results can differ

  • Different implementation: a framework may use a headless shell while headed mode uses regular Chromium.
  • Different channel: Playwright’s chromium channel and its default executable can follow different Headless paths.
  • Browser version drift: a laptop and CI runner may have different Chrome versions.
  • Viewport and device scale: responsive breakpoints, canvas output and screenshots depend on these values.
  • Fonts and graphics: missing fonts, GPU availability and operating-system rendering can alter pixels.
  • Timing: animations, lazy images, network idle and third-party scripts can settle at different times.
  • Environment assumptions: proxies, certificates, locale, timezone, permissions and environment variables may differ.

How to compare modes reliably

  1. Record the framework and exact version.
  2. Record the browser executable, channel and version.
  3. Use identical viewport, scale factor, locale, timezone and user agent.
  4. Wait for a deterministic application state, such as a selector becoming visible.
  5. Disable or freeze animations when taking visual snapshots.
  6. Capture console messages, failed requests and a trace on failure.
  7. Compare DOM state and computed styles before comparing pixels.

Performance, reliability and cost considerations

Headless is usually the operational choice for unattended jobs because it does not require a display server. The supplied sources do not establish a universal speed, memory or reliability percentage. Do not assume that every headless run is faster: startup flags, browser build, page complexity, concurrency and network conditions determine actual performance.

For reliability, pin browser versions, use explicit waits, isolate browser contexts, limit concurrency to the available CPU and memory, and retain failure artifacts. Retry transient navigation failures with a bounded policy; do not hide deterministic selector or authentication errors behind unlimited retries.

Browser automation cost includes compute time, browser downloads, storage for artifacts and engineering time spent maintaining the environment. A managed screenshot API can remove browser installation and orchestration work when you only need an image or PDF.

Common errors and fixes

Error or symptom Likely cause Fix
Failed to connect to bus or display errors Headed Chrome is running where no display exists Run headless in CI, or provide a correctly configured display server such as Xvfb for headed debugging.
Different screenshot in CI Different browser build, fonts, viewport or timing Pin versions and fonts, match channels, set the viewport explicitly and wait for a stable selector.
Missing lazy-loaded content Capture occurred before scrolling or loading completed Scroll or trigger the application’s load behavior, then wait for the content selector.
Browser executable not found Framework browser binaries were not installed Run the installation command for your framework release and cache the resulting browser in CI.
Page hangs at navigation Never-ending requests, blocked third parties or an overly strict wait condition Set a navigation timeout, use a targeted readiness selector and inspect failed requests.
Headless shell behaves differently The shell is a separate implementation Use regular unified Headless or the intended channel, then retest with the exact production configuration.
Blank or partially rendered output JavaScript error, bot check, authentication failure or premature capture Capture console and network logs, verify credentials and wait for a page-specific readiness condition.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need a clean image or PDF without managing Chrome. One GET request returns PNG, JPEG, WebP or PDF output. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
import requests
r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for the full option set, including full-page capture, CSS selectors, device presets, custom viewports, dark mode, retina scale, PDF settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture and usage data. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Is headless Chrome a different browser?

Modern Chrome Headless shares the browser implementation with headed Chrome. A framework may still select the separate legacy chrome-headless-shell, which can behave differently.

Should production tests run headed?

Usually run unattended tests headless, then reproduce failures headed with the same browser channel and version.

Does headless mean JavaScript is disabled?

No. Headless browsers execute JavaScript and support normal automation APIs.

Why does Playwright mention a Chromium channel?

The channel lets you opt into a particular browser distribution and, for chromium, the newer Headless path documented by Playwright.

Can I use headed mode in a container?

Yes, but you need a display environment and suitable permissions. For unattended containers, headless is simpler.