What Is Headless Mode in Selenium?
Headless mode runs Selenium-controlled browsers without a visible window. Learn the current Chrome setup, migration details, debugging tips, and runnable examples.
Headless mode runs a Selenium-controlled browser without displaying its normal browser window. The browser still loads pages, executes JavaScript, applies cookies, and exposes the same WebDriver automation interface. You enable it through the selected browser’s options and command-line arguments; it is not a separate Selenium product.
For current Chrome automation, add --headless=new to ChromeOptions. Selenium deprecated its convenience setHeadless method in version 4.8 and removed it in 4.10, so older examples need updating. The argument is Chrome-specific guidance; Firefox, Edge, and other browsers have their own options and compatibility rules.
How headless mode works
In a headed run, Selenium starts a browser process with a visible window. In a headless run, the browser process starts without presenting that window to the desktop. Your test or scraper still communicates with the browser through WebDriver:
- Your program creates a browser options object.
- You add the browser’s headless argument.
- Selenium starts a local or remote browser session.
- The session navigates, renders, and interacts with pages normally from your code.
- Your program reads the result, saves a screenshot, or quits the session.
Headless does not automatically mean faster, more reliable, or pixel-identical to headed mode. Rendering can vary with browser versions, fonts, GPU configuration, viewport size, operating-system libraries, and page timing. Measure those properties in the environment where your automation runs.
Current Chrome setup
Selenium’s Chrome documentation shows browser arguments being passed through ChromeOptions. The following Python program is a complete minimal example:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
# Selenium Manager can locate a compatible driver in supported environments.
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
print(driver.title)
finally:
driver.quit()
Install the Python binding first with python -m pip install selenium. Keep Chrome and ChromeDriver major versions compatible. Selenium Manager has shipped with Selenium releases since 4.6 and can manage drivers under its documented conditions, but an offline or restricted machine may still need a manually installed browser and driver.
Set a deterministic viewport
Headless sessions do not have a physical monitor to establish a useful window size. Set one explicitly when layout, screenshots, or responsive breakpoints matter:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,900")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
driver.save_screenshot("example.png")
finally:
driver.quit()
Node.js example
In JavaScript, pass the same Chrome argument through Selenium’s Builder and chrome.Options classes:
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
(async function () {
const options = new chrome.Options();
options.addArguments('--headless=new', '--window-size=1440,900');
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
try {
await driver.get('https://example.com');
console.log(await driver.getTitle());
await driver.takeScreenshot().then(data => require('fs').writeFileSync('example.png', data, 'base64'));
} finally {
await driver.quit();
}
})();
Install the binding with npm install selenium-webdriver. The browser and driver still need to be available to the machine or remote endpoint.
Java example
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", "--window-size=1440,900");
WebDriver driver = new ChromeDriver(options);
try {
driver.get("https://example.com");
System.out.println(driver.getTitle());
} finally {
driver.quit();
}
}
}
Headless migration: setHeadless and old flags
Older Selenium examples often use a convenience method such as options.setHeadless(true). Selenium’s project guidance says that method was deprecated in 4.8 and removed in 4.10. Replace it with an explicit browser argument:
# Older style (do not use with current Selenium bindings)
options.set_headless(True)
# Current Chrome style
options.add_argument("--headless=new")
Selenium’s January 2023 migration article describes Chromium’s transition from the traditional --headless mode, to --headless=chrome for Chrome versions 96–108, and then to --headless=new from version 109. Those are historical transition details; check the browser and Selenium documentation that matches the versions you deploy. Selenium 4.18 also documented a Chrome headless naming change and advised switching to --headless=new.
Firefox and other browsers
Do not copy Chrome’s flag blindly to every browser. Selenium documents Firefox-specific options and requires a compatible Firefox and geckodriver combination. A Python Firefox example is:
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument("-headless")
driver = webdriver.Firefox(options=options)
try:
driver.get("https://example.com")
print(driver.title)
finally:
driver.quit()
Verify the current argument, minimum browser version, and driver recommendation for the browser you support. For a remote session, create the appropriate options object and pass it to the remote WebDriver endpoint; the options determine which browser is requested.
Useful options around headless runs
| Need | Typical configuration | Why it matters |
|---|---|---|
| Repeatable layout | --window-size=1440,900 |
Controls responsive breakpoints and screenshot dimensions. |
| High-density output | Set the browser/device scale factor where your binding and browser support it | Changes CSS pixels versus bitmap pixels; verify the result in your environment. |
| Debugging | Temporarily remove the headless argument | Lets you watch navigation and inspect the page interactively. |
| Remote execution | Use a browser options instance with RemoteWebDriver |
Requests the browser capabilities from a Selenium Grid or other endpoint. |
| Long pages | Scroll or use a page-capture strategy in your code | A viewport screenshot is not automatically a full-page screenshot. |
Only add flags you understand and can support. A copied collection of server-oriented flags can hide the real cause of a failure and can alter rendering behavior.
Waiting for page state before capture
Headless mode does not change page-loading semantics. Modern pages may continue rendering after the initial navigation completes. Prefer an explicit condition over a fixed sleep when a known element signals readiness:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,900")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://example.com")
WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.TAG_NAME, "body"))
)
driver.save_screenshot("ready.png")
finally:
driver.quit()
For applications with network-driven content, wait for the element or application state your test actually needs. A long arbitrary delay increases runtime without proving that the correct state is present.
Headless versus headed: what changes?
- Visibility: headless has no visible browser window; headed mode does.
- Configuration: headless requires a browser-specific option or argument.
- Debugging: headed mode is often easier to inspect manually, while headless runs need logs, screenshots, page source, and browser console diagnostics.
- Rendering: do not assume identical pixels across modes or environments. Compare outputs in your target setup.
- Operations: headless is convenient for CI and servers without a desktop session, but it still needs a compatible browser, driver, libraries, fonts, and enough resources.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
AttributeError or missing set_headless |
The binding removed the convenience method. | Use options.add_argument("--headless=new") for Chrome. |
| Session not created; version mismatch | Chrome and ChromeDriver major versions differ. | Check both versions, update the incompatible component, and let Selenium Manager resolve the driver where supported. |
| Chrome cannot start in CI | Missing browser libraries, restricted downloads, or insufficient process permissions. | Install the required browser dependencies, verify the browser launches on that host, and inspect the full driver log. |
| Blank or incomplete screenshot | The page is still rendering, content is lazy-loaded, or navigation reached an error page. | Wait for a meaningful selector, check the current URL and title, and save page source and a diagnostic screenshot. |
| Element is outside the viewport | The requested element has not been scrolled into view or is hidden by responsive layout. | Set the viewport, scroll the element into view, and verify its displayed state before interacting. |
| Different layout in CI | Viewport, device scale, fonts, browser version, or operating-system rendering differs. | Pin the browser environment where practical, set the viewport explicitly, install required fonts, and compare captured artifacts. |
| Firefox flag has no effect | A Chrome argument was copied to Firefox. | Use Firefox’s options and current Selenium Firefox documentation. |
Debugging checklist
- Print the Selenium, browser, and driver versions.
- Record the requested URL and the final URL after redirects.
- Set an explicit window size.
- Save a screenshot and page source immediately after a failure.
- Check browser console and driver logs when JavaScript or navigation fails.
- Run the same code once in headed mode to determine whether the issue is visibility, environment, or page timing.
- Replace fixed sleeps with a condition tied to the page state you require.
Performance, reliability, and cost considerations
The supplied Selenium documentation establishes the configuration and compatibility rules, not a universal speed or reliability advantage for headless mode. Treat performance as an environment-specific measurement. Track navigation time, wait time, screenshot time, memory use, and failure rate with the exact browser version, page set, viewport, and host image you deploy.
For reliability, keep browser and driver versions compatible, make page readiness explicit, allocate enough CPU and memory for concurrent sessions, and retain artifacts from failed runs. For cost, account for browser startup, session duration, machine or CI minutes, bandwidth, and concurrency. Headless removes the need for a displayed desktop window; it does not remove the browser process or its resource usage.
Or skip the browser setup
If your goal is a clean website screenshot rather than browser automation itself, ScreenshotNeo provides a GET API and an MCP server. See the ScreenshotNeo API documentation for the available parameters.
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}`);
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Is headless mode a different browser?
No. It is an execution mode for a browser controlled by Selenium.
Can I use --headless=new with Firefox?
Do not assume so. Use the options and arguments documented for the specific browser and binding.
Why did Selenium remove setHeadless?
The convenience method was deprecated in Selenium 4.8 and removed in 4.10. Browser arguments in options are the current configuration pattern.
Does headless guarantee the same screenshot as headed Chrome?
No. Browser version, fonts, viewport, scale factor, operating system, and page timing can change rendering. Validate in the environment you ship.
Do I need a display server for headless Chrome?
Headless Chrome is designed to run without a visible browser window. You still need a working browser installation and its runtime dependencies.
What should I capture when a headless test fails?
Keep the driver log, browser and driver versions, final URL, page source, console output, and a screenshot from the failure point.


