Chrome Headless Mode Changes: What Selenium Users Need to Know
Chrome 132 removed legacy Headless from the Chrome binary. Learn which flag to use, how to update Selenium options, and when Headless Shell makes sense.
For current Chrome, pass --headless through Selenium’s Chrome options. Chrome’s unified Headless mode arrived in Chrome 112 and uses the main Chrome browser implementation. Chrome 132 removed the old Headless implementation from the Chrome binary, so --headless=old no longer launches it. If your script uses Selenium’s removed setHeadless(true) convenience method, replace it with an explicit browser argument.
This is two related but separate changes: Chrome changed which Headless implementation its binary runs, and Selenium changed how its language bindings express the option. The Selenium convenience methods were deprecated in Selenium 4.8 and removed in 4.10.
1. What changed, and when?
| Version | Change | What it means for your script |
|---|---|---|
| Chrome 112 | Chrome introduced unified Headless. It creates platform windows without displaying them and shares the main browser implementation with headful Chrome. | Use the regular Chrome binary with --headless for modern Headless runs. |
| Selenium 4.8 | The project deprecated convenience methods that set Headless. | Move the choice into the browser options as an argument. |
| Selenium 4.10 | The deprecated convenience methods were removed. | Calls such as setHeadless(true) must be replaced with the binding’s Chrome options API. |
| Chrome 132 | The old Headless implementation was removed from the Chrome binary. --headless=old prints an error; --headless and --headless=new use unified Headless. |
Drop --headless=old, unless you deliberately switch to the separate chrome-headless-shell binary. |
Chrome’s current guidance is to use --headless. The older transition spelling --headless=new also selects unified Headless, but plain --headless is the straightforward current setting. [Chrome Headless mode] [Chrome 132 removal announcement] [Selenium migration announcement]
2. Update Selenium’s Chrome options
Use the Chrome options class for your binding and add the --headless argument. These examples show the current pattern; ensure Chrome and ChromeDriver are installed and compatible with your Selenium setup.
Python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
# Optional: set a predictable viewport for layout-sensitive captures.
options.add_argument("--window-size=1440,1000")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
print(driver.title)
finally:
driver.quit()
Java
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");
WebDriver driver = new ChromeDriver(options);
try {
driver.get("https://example.com");
System.out.println(driver.getTitle());
} finally {
driver.quit();
}
}
}
JavaScript (Selenium WebDriver)
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
(async () => {
const options = new chrome.Options();
options.addArguments('--headless');
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
try {
await driver.get('https://example.com');
console.log(await driver.getTitle());
} finally {
await driver.quit();
}
})();
C#
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
var options = new ChromeOptions();
options.AddArgument("--headless");
using var driver = new ChromeDriver(options);
driver.Navigate().GoToUrl("https://example.com");
Console.WriteLine(driver.Title);
Ruby
require "selenium-webdriver"
options = Selenium::WebDriver::Options.chrome(args: ["--headless"])
driver = Selenium::WebDriver.for(:chrome, options: options)
begin
driver.navigate.to("https://example.com")
puts driver.title
ensure
driver.quit
end
Exact method spelling and supported options can vary with a binding’s version. Consult its current API documentation if the constructor form differs in your installed release. The common migration is the same: add a Chrome command-line argument through Chrome options, rather than calling a Selenium Headless convenience method. Selenium’s transition post has binding-specific examples, including --headless=new examples from that period. [Selenium: Headless is Going Away!]
3. Choose unified Headless or Headless Shell
For most Selenium tests, start with unified Headless in the regular Chrome binary. It exercises the same Chrome browser implementation used in headful mode, which suits end-to-end tests that need browser feature coverage and behavior close to a normal Chrome run.
Chrome also distributes chrome-headless-shell, which retains the old Headless implementation outside the Chrome browser binary. Consider it when an existing workload specifically depends on old Headless behavior or when its smaller dependency footprint fits the environment. Chrome describes Shell as a lightweight wrapper around Chromium’s content module; it does not require X11/Wayland or D-Bus and may be more performant for some screenshot or scraping tasks. These are qualitative descriptions, not a quantified performance guarantee. Shell does not provide the same full Chrome feature coverage, so validate any compatibility-dependent test before adopting it. [Chrome Headless Shell documentation]
| Need | Starting choice |
|---|---|
| Match the main Chrome browser implementation and test web-app behavior | Unified Headless with --headless |
| Preserve a dependency on behavior specific to old Headless | Evaluate chrome-headless-shell |
| Run in a minimal environment | Compare the runtime dependencies of your chosen Chrome build and Shell; Shell has a smaller dependency footprint according to Chrome’s documentation |
| Test extensions or use broader browser functionality | Prefer unified Headless in Chrome |
4. Remove obsolete assumptions and flags
- Remove
--headless=old. Chrome 132 and later do not launch the old implementation from the Chrome binary. - Use
--headlessas the default. Keep--headless=newonly if it is useful for compatibility with a versioned setup; it selects unified Headless in Chrome 132. - Do not add Xvfb just because the browser is headless. Chrome’s Headless Shell documentation says a display server such as Xvfb is not needed for Headless Chrome.
- Do not carry
--disable-gpuforward blindly. Chrome documents it as a temporary workaround for a few bugs and says it is needed only on Windows in the described context. Check your platform and browser version before adding it. - Keep Chrome and ChromeDriver setup aligned. Review the release guidance for your versions when upgrading; driver discovery and legacy Headless workarounds have changed over time.
Sources: Chrome Headless Shell environment notes and ChromeDriver downloads and release notes.
5. Verify a migration safely
- Record the Chrome, ChromeDriver, Selenium, and binding versions used by the job.
- Replace Selenium’s removed Headless convenience call with
--headlessin Chrome options. - Remove
--headless=oldand investigate any other legacy flags individually. - Run a small representative test in the same container or host image used in CI. Check navigation, title or DOM assertions, downloads if relevant, and screenshot output.
- If a visual result changed, compare viewport size, device scale, fonts, and page readiness before blaming the Headless implementation. Make the viewport explicit when layout is sensitive.
- If a test relies on old Headless behavior, run a controlled evaluation with Headless Shell and decide whether that dependency is intentional and supportable.
- Repeat the check when upgrading Chrome or ChromeDriver, using the official release notes for the versions in use.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Chrome prints an error for --headless=old or exits during startup |
The Chrome binary is version 132 or newer; the old implementation was removed from it. | Use --headless for unified Headless, or use the separate Headless Shell when old implementation behavior is required. |
Compilation or runtime error says setHeadless is missing |
The Selenium convenience API was removed in Selenium 4.10 after deprecation in 4.8. | Build the binding’s Chrome options object and add --headless. |
| Browser launches, but a screenshot differs from the old run | Unified Headless is a different implementation from legacy Headless, or the viewport/readiness conditions differ. | Set a fixed viewport, wait for the page’s required element or state, and inspect the output. If the job truly depends on legacy behavior, evaluate Headless Shell. |
| ChromeDriver cannot start or reports a session creation error | Driver/browser compatibility, binary discovery, permissions, or environment setup may be wrong; the error alone does not identify which. | Check the installed browser and driver versions and consult ChromeDriver’s version-specific release guidance. Confirm the selected binary is the intended Chrome or Shell executable. |
| CI fails with a message about a missing display | A wrapper or environment configuration may still be trying to start a display server, or another browser setting expects one. | Headless Chrome does not generally require Xvfb. Remove that dependency only after checking the rest of the job does not need a display for another tool. |
A copied setup includes --disable-gpu and still behaves unexpectedly |
The flag is not a general fix for Headless problems. | Remove it unless the platform and Chrome version have a documented need; Chrome describes it as a temporary workaround for a few bugs. |
7. Performance, reliability, and cost
There is no quantified benchmark in the reviewed Chrome or Selenium material for unified Headless versus Headless Shell. Chrome says Shell may be more performant for some automated screenshot or scraping workloads, while unified Headless offers the main Chrome implementation and broader fidelity. Treat that as a workload-dependent choice: measure your own job if runtime or resource use drives the decision.
For reliable CI runs, pin and record browser and driver versions, use explicit Chrome options, set a viewport when rendering matters, and wait for a meaningful page condition rather than assuming navigation completion means the page is visually ready. Keep a small upgrade check that exercises the pages and features your suite depends on. These steps make version changes easier to diagnose; they do not guarantee identical rendering across hosts.
The Chrome/Selenium configuration itself has no price specified by the cited sources. Infrastructure cost depends on where and how many browser processes you run. If the actual task is simply to obtain website screenshots rather than exercise a Selenium workflow, an API can avoid maintaining a browser process for that capture. ScreenshotNeo is a website screenshot API and MCP server from Yorker Media; its site is ScreenshotNeo.
8. Or skip the browser setup
If you need a screenshot rather than a Selenium test, ScreenshotNeo takes a URL in one API request and returns an image or PDF. See the ScreenshotNeo API documentation.
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}`);
- Cookie banners are accepted and removed before capture, along with known consent platforms, newsletter popups, and chat widgets; each step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdffor AI agents and MCP clients. - The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.
9. FAQ
Does --headless=new still work?
Chrome 132 runs unified Headless with both --headless and --headless=new. Use plain --headless for a new configuration.
Can I keep using setHeadless(true)?
Not with Selenium 4.10 and later, where the convenience methods were removed. Add the argument through Chrome options instead.
Is Headless the same browser as headful Chrome?
Unified Headless shares Chrome’s main browser implementation. It does not display its platform windows, but is intended to provide the real Chrome browser implementation in Headless operation.
Should I switch every old test to Headless Shell?
No. Shell is for workloads that need the old implementation or benefit from its footprint. Start with unified Headless unless your test has a demonstrated compatibility need.
Do I need Xvfb?
Chrome’s documentation says a display server such as Xvfb is not needed for Headless Chrome. Check whether another component in your job needs one before removing it.


