Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run Selenium headlessly by adding the browser’s headless argument before creating the driver. For Chrome, use --headless=new, set an explicit viewport, perform the same navigation and waits as a visible test, and always call quit() in cleanup. Current Selenium releases can usually obtain a compatible driver through Selenium Manager, so you do not have to download ChromeDriver manually.

What headless Selenium actually does

Headless mode runs a real browser without displaying its normal graphical window. It still creates a browser session, renders HTML and CSS, executes JavaScript, loads resources, applies responsive breakpoints, and exposes the DOM to Selenium. Headless does not mean “HTML-only” or “JavaScript disabled.”

Chrome for Developers documents a change introduced in Chrome 112: Chrome’s current headless mode creates platform windows but does not display them. This shares the normal Chrome code path more closely than the older headless implementation. The documentation page was last updated on October 21, 2024.

The practical consequence is that your test logic normally stays the same. You change browser startup options, then continue to use get(), locators, explicit waits, assertions, downloads, and screenshots. Differences usually come from viewport size, available fonts or libraries in a CI image, permissions, and timing—not from Selenium skipping page execution.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prerequisites and driver choices

Install Selenium and a browser

Install the Selenium binding for your language and make sure a supported browser is installed in the same host or container where the test runs. A headless flag cannot provide a browser binary that is missing from the runtime image.

  • Python: install the Selenium package in the environment that runs the script.
  • Java: add the Selenium Java dependency to your build and ensure Chrome, Firefox, or Edge is installed.
  • CI containers: verify the browser can start as the same user that executes the job. A browser installed only on a developer workstation is not available to a remote runner.

Prefer Selenium Manager

Selenium Manager is shipped with Selenium. When a driver is not already available, the bindings can use it to discover the browser version, resolve a compatible driver, download it, and cache it. This is the lowest-maintenance option for most local and CI runs.

When manual ChromeDriver management is appropriate

If your organization pins every binary, supplies drivers through an internal image, or cannot allow runtime downloads, configure the driver path yourself. Keep the ChromeDriver major version aligned with the installed Chrome major version. A mismatch commonly produces a “session not created” error before your test code runs.

Run Chrome headlessly with Python

Create an Options object, add --headless=new, set a deterministic viewport, and pass the options when constructing webdriver.Chrome. Put cleanup in a finally block so a failed assertion does not leave a browser process behind.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()

The viewport argument is important. Without an explicit size, the page can choose a different responsive breakpoint than it does in headed development. That can change navigation menus, element visibility, and layout-dependent assertions.

Add a reliable wait instead of sleeping blindly

Headless execution can expose timing assumptions that were hidden by a slower interactive run. Use an explicit condition tied to the application state.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 15)
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "main")))

Choose a selector or state that means the page is ready for the next action. A fixed delay can still be too short on a busy CI runner and unnecessarily slow on a fast one.

Capture evidence when a test fails

from pathlib import Path

try:
    driver.get("https://example.com")
    # test steps and assertions here
except Exception:
    Path("failure.png").write_bytes(driver.get_screenshot_as_file("failure.png") and b"")
    raise
finally:
    driver.quit()

A simpler and more portable form is:

try:
    driver.get("https://example.com")
    # assertions
except Exception:
    driver.save_screenshot("failure.png")
    raise
finally:
    driver.quit()

Run Chrome headlessly with Java

Java uses ChromeOptions in the same place Python uses Options. Construct ChromeDriver with those options before calling get.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class HeadlessExample {
    public static void main(String[] args) {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless=new");
        options.addArguments("--window-size=1920,1080");

        WebDriver driver = new ChromeDriver(options);
        try {
            driver.get("https://example.com");
            System.out.println(driver.getTitle());
        } finally {
            driver.quit();
        }
    }
}

Selenium Manager is invoked by current Selenium bindings when a suitable driver is not already configured. If you intentionally provide a driver service or executable path, verify its browser-major-version compatibility before investigating the test itself.

Firefox and Edge headless sessions

Firefox

Use the browser-specific options class and pass the headless setting before creating the driver.

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")
options.add_argument("--width=1920")
options.add_argument("--height=1080")

driver = webdriver.Firefox(options=options)
try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()

Edge

Edge is Chromium-based, so use EdgeOptions and the Chromium headless argument.

from selenium import webdriver
from selenium.webdriver.edge.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")

driver = webdriver.Edge(options=options)
try:
    driver.get("https://example.com")
    print(driver.title)
finally:
    driver.quit()

Selenium Manager supports Chrome, Firefox, and Edge. The exact browser option class is the part that changes; navigation, waits, assertions, and cleanup remain the same.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make headless runs deterministic

Set the viewport and profile deliberately

Use the same width and height in local debugging and CI. If your application behaves differently for a signed-in user, create or load the same test profile or set cookies through WebDriver; do not assume a developer’s graphical profile exists on the runner.

Wait for application state

Wait for a visible element, a URL change, an enabled control, or another condition that represents readiness. If content appears after network activity, wait for the relevant element rather than assuming that get() means every client-side request has finished.

Keep browser actions inside the session lifetime

Perform navigation, downloads, assertions, and screenshots before quit(). Put cleanup in finally (Python) or a Java finally block so failures do not accumulate orphaned browser processes across a test suite.

Use logs for CI-only failures

If a run crashes only in CI, enable ChromeDriver service logging and preserve the log as a build artifact. Selenium’s Chrome documentation shows Python service logging controls such as webdriver.ChromeService(log_output=...). The log can distinguish a driver startup problem from a page-level timeout.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Headless execution choices

Decision Use this when Important trade-off
Local headed browser You are stepping through selectors or watching a failure interactively. Requires a graphical session; it is slower to reproduce unattended CI conditions.
Local headless browser You want fast, repeatable command-line runs without opening a window. You need screenshots, logs, and explicit viewport settings to inspect failures.
Headless CI runner Tests must run on every commit or on a scheduled build. The image must contain a supported browser, fonts and permissions; driver and browser versions must remain compatible.
Selenium Manager You can allow Selenium to resolve and cache a driver. Driver availability depends on the runner being able to perform the required discovery or download.
Manually pinned driver Your build image is controlled and runtime downloads are restricted. You must update the driver when the browser major version changes.

Troubleshooting headless Selenium

“Session not created” or a version-mismatch message

Check the installed Chrome and ChromeDriver major versions. Remove a stale manually configured driver path and let Selenium Manager resolve the pair, or update the pinned driver and browser together. The same principle applies when a CI image silently updates its browser.

An element is missing only in headless mode

First set an explicit window size. A narrow default viewport can activate a mobile breakpoint and hide the desktop control. Then replace arbitrary sleeps with an explicit wait for the element’s actual state. Save a screenshot and page source at the failure point to determine whether the element is absent, hidden, or simply not ready.

Chrome exits immediately in CI

Inspect the ChromeDriver service log and the CI job’s browser-startup output. Confirm that Chrome is installed, executable by the job user, and allowed to create its required runtime files. Compare the same image and command locally if possible. Temporarily remove --headless=new on a machine with a graphical session to see whether the failure is browser startup or page logic.

A test passes headed but times out headlessly

Use the same viewport, profile, locale and test data in both modes. Add a wait for the application’s ready state rather than waiting a fixed number of seconds. Capture a screenshot immediately before the timeout; responsive layout or a consent dialog can block the expected control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

An old tutorial uses options.headless = True

Current Selenium guidance favors explicit browser command-line arguments. For Chrome, use options.add_argument("--headless=new") before creating the driver. This makes the selected Chromium headless mode visible in code and easier to review.

You need to inspect a failure visually

Remove the headless argument temporarily, keep the same viewport and profile settings, and run the exact failing test on a machine with a graphical session. Compare that run with the saved headless screenshot and driver log. Restore headless mode after diagnosing the page behavior.

Performance, reliability, and cost considerations

Headless removes the visible window; it does not guarantee a particular speed or memory saving. The reviewed Chrome and Selenium documentation does not publish a qualifying universal benchmark for headless Selenium’s adoption, speed, or resource reduction. Treat performance as an environment-specific measurement.

  • Measure your own suite with the same browser version, viewport, test data, and CI image.
  • Reuse a driver only when your test isolation model allows it; otherwise create and quit sessions per test or fixture as appropriate.
  • Keep screenshots and driver logs only for failures if artifact storage is limited.
  • Pin the browser image or monitor browser updates so a major-version change does not surprise a manually managed driver.
  • Use explicit waits and avoid needless fixed delays; this improves both reliability and elapsed time without changing browser semantics.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a clean screenshot or PDF rather than interactive WebDriver actions, ScreenshotNeo provides a website screenshot API and MCP server. A single request can return PNG, JPEG, WebP, or PDF without installing Selenium, a browser, or a driver.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL (the API documentation is at https://screenshotneo.com/docs/):

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

Python:

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

Node.js:

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

Why this is different from a bare browser screenshot

  • Before capture, ScreenshotNeo accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each cleanup step can be turned off.
  • Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers identify the page verdict and whether the request was billed.
  • An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
  • Options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, click-before-capture, selector or network-idle waits, request and resource blocking, custom headers, cookies, user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.

Every feature is available on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it without a card.

FAQ

Can a headless session still execute JavaScript?

Yes. Headless Chrome is still Chrome: scripts run, the DOM is rendered, and Selenium can interact with the resulting page. A failure usually points to timing, viewport, profile, or environment differences rather than JavaScript being disabled.

Do I need to call a separate “headless” Selenium API?

No. Headless behavior is selected through the browser’s options object before the driver is constructed. The WebDriver API used after startup is the same one used in a visible session.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I archive from a failed CI job?

Keep the WebDriver or browser service log, a screenshot taken at the failure point, and the page source when practical. Together they show whether startup failed, the layout changed, or the application had not reached the expected state.

Frequently Asked Questions

Can a headless session still execute JavaScript?

Yes. Headless Chrome remains a full browser, so scripts execute and the DOM is rendered.

Do I need a separate Selenium API for headless mode?

No. Select headless behavior in the browser options before constructing the driver; subsequent WebDriver calls are unchanged.

What artifacts are most useful for a CI failure?

Archive the driver log, a screenshot captured at failure, and page source when available.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.