ScreenshotNeo

BlogComparisons

Chrome Headless vs Selenium for Capturing Screenshots of Web Apps

Compare direct Chrome Headless captures with Selenium-driven Chrome, with runnable examples, setup guidance, troubleshooting, and a practical choice guide.

By the ScreenshotNeo team4 October 20269 min read

Short answer: Use Chrome Headless directly for a screenshot of a known URL at a known viewport. Use Selenium when the screenshot depends on browser actions such as logging in, clicking through the app, changing state, or capturing several points in a test. They are not mutually exclusive: Selenium can drive Chrome in headless mode.

There is no universal speed or image-fidelity winner. Choose based on the steps needed to reach the page state, your existing test setup, and how tightly you need to control browser versions.

1. How the two approaches differ

Question Direct Chrome Headless Selenium driving Chrome
What is it? Chrome running without a visible UI, invoked with command-line flags. A WebDriver automation framework that sends browser commands through ChromeDriver.
How is a screenshot saved? Chrome’s --screenshot flag writes an image file. Call a Selenium screenshot API after navigating and preparing the page.
Best fit A direct capture when URL, viewport, and readiness delay are known. A capture that requires login, clicks, form input, assertions, multiple states, or an existing automated test.
Main setup Install or locate Chrome; choose flags and the working directory. Install Selenium, configure Chrome options, and keep Chrome and ChromeDriver compatible.
Browser scope Chrome-specific command-line workflow. WebDriver framework supporting multiple browser implementations; examples below use Chrome.

These differences follow the documented roles of Chrome Headless and Selenium. The recommendation by workflow complexity is an inference from those capabilities, not a benchmark.

2. Capture a simple page with Chrome Headless

Modern Chrome Headless runs the Chrome browser implementation without a visible UI. The basic command-line flow is:

chrome --headless --screenshot --window-size=1280,900 --timeout=3000 https://example.com

Chrome writes screenshot.png in the current working directory. Replace chrome with the executable path if it is not on your PATH. The viewport dimensions are in CSS pixels. The three-second timeout is only an example: it delays capture and does not guarantee that every network request, font, animation, or lazy image has finished.

Useful command-line controls

Control Use Limit
--headless Run Chrome without displaying its UI. Use flags supported by the Chrome version installed in the environment.
--screenshot Save a screenshot as screenshot.png. Check the process working directory to find the output.
--window-size=WIDTH,HEIGHT Set the capture viewport, such as --window-size=412,892. This sets the viewport; it does not by itself emulate every property of a physical device.
--timeout=MILLISECONDS Wait the specified time before capture. A fixed delay can be too short on a slow page and waste time on a fast one.
--virtual-time-budget=MILLISECONDS Allow time-dependent page code to execute against virtual time, often faster than real time. It is not proof that real network activity or app-specific readiness has completed.

For a dynamic app, use an application-specific readiness condition rather than relying only on a fixed wait. If the page needs interaction, a command-line capture may not be enough; use Selenium or another automation layer to reach the desired state first.

3. Capture a web app state with Selenium and headless Chrome

This Python example uses Selenium 4, launches Chrome headlessly, opens a URL, waits for a CSS selector that indicates the page is ready, and saves a PNG. Install Selenium with python -m pip install selenium. You also need Chrome and a compatible ChromeDriver available to Selenium in your environment.

from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait

url = "https://example.com/dashboard"
ready_selector = "main[data-loaded='true']"
output = Path("dashboard.png")

options = Options()
options.add_argument("--headless")
options.add_argument("--window-size=1440,1000")

# Keep the driver open only for the duration of this capture.
with webdriver.Chrome(options=options) as driver:
    driver.get(url)
    WebDriverWait(driver, 20).until(
        lambda browser: browser.find_element("css selector", ready_selector)
    )
    driver.save_screenshot(str(output))

print(f"Saved {output.resolve()}")

Replace ready_selector with an element or state that means the content you need is actually ready. If authentication is required, add the login steps before the wait. Do not put real credentials in source code; load them from your environment or your CI secret store.

Full-page and element captures

save_screenshot() captures the current browser viewport. For a specific element, locate it and call the element’s screenshot method:

element = driver.find_element("css selector", "main .report")
element.screenshot("report.png")

Full-page capture is not a single portable WebDriver behavior across browsers and versions. For Chrome-specific full-page output, use the Chrome DevTools Protocol or a supported browser tool, and verify the result in the pinned Chrome version. A simple scroll-and-stitch approach can duplicate sticky headers, miss content loaded only after scrolling, or introduce seams; use it only when those tradeoffs are acceptable.

Reproducible CI setup

  1. Choose a Chrome version and pin it for the job.
  2. Use a matching ChromeDriver version. Chrome documents that Chrome and ChromeDriver major versions must match.
  3. Keep viewport, device scale factor, operating system, fonts, color scheme, locale, page data, and readiness condition stable when comparing captures.
  4. Update the browser and driver intentionally, then review screenshot changes as part of that update.

Chrome for Testing provides versioned browser and matching ChromeDriver downloads for controlled automation environments. ChromeDriver is the bridge between WebDriver frameworks and Chrome. Pinning the pair helps prevent an untracked browser update from changing rendering or breaking automation.

4. Choosing between them

Your capture needs Recommended starting point Reason
One public URL and a fixed viewport Direct Chrome Headless Few moving parts when the page is ready without interaction.
A page that requires login or a sequence of clicks Selenium with headless Chrome WebDriver can perform the steps before saving the screenshot.
Screenshots are part of browser tests Selenium with headless Chrome Capture can share navigation, setup, and assertions with the test workflow.
Several screenshots of different states in one session Selenium with headless Chrome Keep one browser session and capture after each state change.
Need the simplest local command for a static page Direct Chrome Headless A shell invocation can be enough.
Need another browser implementation Selenium WebDriver supports multiple browser implementations; check the chosen browser’s own setup and behavior.

Both approaches can run unattended. Selenium can launch Chrome headlessly, so “Headless versus Selenium” is often really “direct browser command versus automation framework.”

5. Readiness, rendering, and edge cases

  • Network activity is not the same as visual readiness. A page can stop making requests before a chart renders, or keep polling after the useful content is visible. Prefer a selector or application state that represents the content you need.
  • Lazy loading may need scrolling. Images and sections can load only when they approach the viewport. Scroll the relevant areas before capture and wait for the content to appear.
  • Animations can make captures inconsistent. Wait for the desired state or disable animations in a controlled test environment using the app’s supported settings or injected test CSS.
  • Fonts affect layout. Install the expected fonts in CI and wait for font loading where typography matters.
  • Viewport is not device emulation. A width and height alone do not configure touch, device pixel ratio, or mobile user agent. Configure those explicitly if your test depends on them.
  • Authenticated pages may expire or redirect. Confirm the final URL and a logged-in marker before capturing. Treat cookies and credentials as secrets.
  • Cross-origin or protected content may not render. Check browser console and network errors when embedded frames, blocked resources, or access controls are involved.
  • Headless is not a guarantee of identical output. For meaningful comparisons, hold Chrome build, OS, fonts, viewport, device scale factor, color scheme, page state, and readiness condition constant.

6. Troubleshooting

Symptom Likely cause Fix
chrome: command not found or process launch fails Chrome is not installed or its binary is not on PATH. Install Chrome in the environment or configure the explicit binary path supported by your Selenium setup. Check executable permissions.
ChromeDriver reports a session creation or version error Chrome and ChromeDriver versions are incompatible, or the driver is unavailable. Use a matching ChromeDriver major version, pin both versions, and ensure the driver can be found by Selenium.
Screenshot is blank or shows a loading screen The capture happened before the app became ready, or navigation redirected to an error/login page. Wait for an app-specific selector, inspect the final URL, and verify authentication and resource loading.
Output file cannot be found The command wrote it to a different current working directory. Set the job’s working directory deliberately and print or resolve the output path.
Images or lower sections are missing Lazy loading or viewport-only capture. Scroll the page or target the needed region, wait for its content, and use a full-page method suitable for the browser.
Screenshot differs between local and CI Different browser, OS, fonts, viewport, device scale factor, color scheme, data, or timing. Pin environment inputs and readiness; compare the same app state before diagnosing a browser defect.
Fixed timeout still produces inconsistent results Load time varies, or a delay does not correspond to the app’s ready state. Replace or supplement the delay with a selector/state wait and use a bounded timeout that fails clearly.
Headless flag behaves differently than expected Instructions target an older Chrome version or obsolete Headless mode. Use current Chrome documentation and check flags against the exact installed version. Modern Chrome uses --headless; old Headless became a separate chrome-headless-shell binary starting with Chrome 132.0.6793.0.

7. Performance, reliability, and cost

This comparison has no controlled benchmark, so it cannot establish that one approach is faster, uses less memory, or produces more accurate pixels. Direct Chrome avoids a separate WebDriver interaction layer for a simple capture, while Selenium adds setup and automation steps that are useful when interaction is required. The practical cost depends on your runtime, browser infrastructure, maintenance, and how many states you capture.

For reliability, pin browser and driver versions, give each capture an explicit readiness condition, bound waits with timeouts, and record the final URL and failure reason. In CI, make browser updates deliberate. A screenshot should be treated as evidence of a particular app state and environment, not as a guarantee that another machine will render every pixel identically.

8. Or skip the browser setup

If you only need a screenshot by URL, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. Its clean-capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each of those steps can be turned off.

Here is the one-call cURL example. See the ScreenshotNeo API documentation for request options and response details:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Equivalent Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo reports the page verdict and billing status in response headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is available on every plan.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

9. FAQ

Can Selenium take screenshots in headless mode?

Yes. Configure Chrome with a headless argument in ChromeOptions, then use Selenium’s screenshot methods after reaching the desired page state.

Are Chrome Headless and Selenium alternatives?

They operate at different layers. Chrome Headless is a browser mode; Selenium is an automation framework that can control headless Chrome.

Does a command-line screenshot capture the whole page?

The documented basic flag saves a screenshot, while viewport dimensions configure the window. For a long page, use and verify a full-page capture method for your browser version, or capture specific content.

Should I use --headless=old?

That advice is historical. Current Chrome Headless uses the modern Chrome implementation; since Chrome 132.0.6793.0, the old implementation is distributed separately as chrome-headless-shell.

Will Selenium make the screenshot pixels match another machine?

No. Selenium controls browser actions; consistent rendering still depends on browser build, operating system, fonts, viewport, device scale factor, color scheme, page data, and timing.

Sources