β€’
Drizz is now Live on ProductHunt! Support Us with Upvotes and Comments
β€’
Upvote now
β€’
Drizz is now Live on ProductHunt! Support Us with Upvotes and Comments
Upvote now
Logo
Schedule a demo
Blog page
>
Running Selenium Headless in Chrome: Setup, Code, and Key Flags

Running Selenium Headless in Chrome: Setup, Code, and Key Flags

How to run Chrome in headless mode with Selenium using --headless=new. Covers Python, Java, and JavaScript setup, the Chrome 112+ changes, CI/CD flags, and why your headless tests might be failing.
Author:
Zaid Abdul Bari
Posted on:
May 20, 2026
Read time:

Headless Chrome runs full Chrome browser without opening a visible window. Same rendering engine, same JavaScript execution, same DevTools protocol. The only difference: no pixels painted to your screen. This makes tests faster (no GPU rendering overhead), uses less memory, and works on CI/CD servers that don't have a display.

If you search for "Selenium headless Chrome," half results show deprecated code. The setHeadless(true) method was removed in Selenium 4.10.0 (released mid-2023). The old --headless flag now launches a completely different binary since Chrome 132. This guide covers what actually works today.

The headless mode timeline (why old tutorials are wrong)

Chrome's headless mode has changed three times, and each change broke something in existing tutorials.

Chrome 59-95 (2017-2021): Original headless mode. A separate browser implementation shipped inside same Chrome binary. It shared Blink rendering engine but had its own code for everything else. This meant bugs that only appeared in headless mode, features that worked in regular Chrome but not in headless, and behavior differences that made headless tests unreliable. The Selenium team's official blog confirmed this was essentially a different browser pretending to be Chrome.

Chrome 96-108 (2021-2022): --headless=chrome flag. Google introduced a new headless mode that used actual Chrome browser code instead of separate implementation. You accessed it with --headless=chrome. The old --headless flag still worked but used old implementation.

Chrome 109+ (2023-present): --headless=new flag. The flag name changed from --headless=chrome to --headless=new. Same new implementation, different flag name. The Chromium team's documentation at developer.chrome.com confirms this is recommended approach.

Chrome 112 (2023): Unified headless. Chrome stopped maintaining old headless implementation entirely. The --headless flag (without =new) now uses new implementation by default. Both headless and headed modes run same code. According to Google's Chrome for Developers documentation, Chrome 112 "creates, but doesn't display, any platform windows" while keeping all browser functions available.

Chrome 132+ (2024): Old headless becomes a separate binary. The original headless implementation is now only available as a standalone binary called chrome-headless-shell. If your tests depend on old behavior, you need to download it separately. For everyone else, --headless=new (or just --headless on Chrome 112+) is way forward.

What this means for your code: If you're using Chrome 112 or later with Selenium 4.10.0 or later, use --headless=new. Don't use options.setHeadless(true) (removed) or bare --headless (works but --headless=new is explicit and future-proof).

Working code: Python, Java, JavaScript

Python

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)
driver.get("https://example.com")
print(driver.title)
driver.quit()

Selenium 4.6+ includes Selenium Manager, which downloads correct ChromeDriver automatically. You don't need to install ChromeDriver manually or specify its path.

Java

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");
options.addArguments("--window-size=1920,1080");

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

JavaScript (Node.js)

const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');

const options = new chrome.Options();
options.addArguments('--headless=new');
options.addArguments('--window-size=1920,1080');

const driver = await new Builder()
  .forBrowser('chrome')
  .setChromeOptions(options)
  .build();

await driver.get('https://example.com');
console.log(await driver.getTitle());
await driver.quit();

All three examples do same thing: launch Chrome without a visible window, navigate to a page, print title, and close browser.

The CI/CD flags you need (and what each one does)

Most headless Chrome tutorials throw a wall of flags at you without explaining them. Here's what each flag does and whether you actually need it.

--headless=new (required). Runs Chrome without a visible window. The =new part tells Chrome to use modern headless implementation that matches headed Chrome behavior.

--window-size=1920,1080 (recommended). Sets viewport size. Without this, headless Chrome defaults to 800x600, which means your responsive CSS triggers mobile/tablet layouts instead of desktop. Screenshots and visual tests will look wrong. Always set this explicitly.

--no-sandbox (CI/CD only). Disables Chrome's sandbox security layer. Required when running as root in Docker containers (sandbox can't run inside another sandbox). Don't use this on your local machine. It reduces security. Only use it in containerized environments where you understand trade-off.

--disable-dev-shm-usage (CI/CD only). Chrome uses /dev/shm (shared memory) for temporary files. In Docker, /dev/shm defaults to 64MB, which isn't enough for Chrome. This flag tells Chrome to use /tmp instead. Without it, Chrome crashes on pages with heavy DOM or multiple tabs. Standard fix for GitHub Actions, Jenkins, GitLab CI, and any Docker-based runner.

--disable-gpu (usually unnecessary). Disables GPU hardware acceleration. This was required on older Windows headless Chrome (pre-2019) to avoid crashes. On modern Chrome (109+), headless mode handles GPU correctly. You can still add it as a safety net, but it's not needed on Linux CI servers or modern macOS/Windows.

--remote-debugging-port=9222 (debugging only). Opens a DevTools debugging port. Connect to localhost:9222 in a headed Chrome instance to inspect what's happening in your headless browser. Useful when a test passes in headed mode but fails in headless.

A complete CI/CD example (Python + GitHub Actions)

# .github/workflows/selenium-tests.yml
name: Selenium Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install selenium pytest

      - name: Run tests
        run: pytest tests/ -v
# tests/test_homepage.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

@pytest.fixture
def driver():
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--window-size=1920,1080")

    driver = webdriver.Chrome(options=options)
    yield driver
    driver.quit()

def test_homepage_title(driver):
    driver.get("https://example.com")
    assert "Example" in driver.title

def test_homepage_has_heading(driver):
    driver.get("https://example.com")
    h1 = driver.find_element("tag name", "h1")
    assert h1.text != ""

‍

‍

GitHub Actions runners come with Chrome and ChromeDriver pre-installed. Selenium Manager handles version matching. You don't need to install either manually.

Why headless tests fail (and how to fix each one)

"Element not interactable" errors

Headless Chrome defaults to 800x600 if you don't set --window-size. At that resolution, elements might be off-screen or hidden behind a responsive navigation menu. The element exists in DOM, but Chrome considers it not interactable because it's not visible in viewport. Fix: add --window-size=1920,1080.

Tests pass locally, fail in CI

Your local machine has fonts, locale settings, and timezone data that CI server doesn't. This causes pixel-level differences in screenshots and text rendering mismatches. Fix: set timezone and locale explicitly in your test setup, or use Docker with a known base image.

Chrome crashes with "DevToolsActivePort file doesn't exist"

This happens in Docker when Chrome can't write to shared memory. Fix: add both --no-sandbox and --disable-dev-shm-usage flags. If issue persists, make sure your Docker container has enough memory allocated (at least 512MB for Chrome).

Different behavior between headed and headless

Before Chrome 112, headless mode was a different implementation with different bugs. If you're running Chrome 111 or earlier, upgrade. On Chrome 112+, headless and headed modes share same code. If you still see differences, check whether your page uses navigator.webdriver detection (some sites block automation) or browser permissions (camera, microphone, notifications work differently in headless).

Screenshots are blank or wrong size

Headless Chrome renders at viewport size you set. If you didn't set one, it's 800x600. If your page has lazy-loaded images or CSS animations, take screenshot after waiting for page to fully load, not immediately after driver.get(). Use explicit waits for elements you need to appear.

Headless Chrome vs headed Chrome: when to use which

Use headless for: CI/CD pipelines, regression test suites that run on every commit, web scraping, PDF generation, scheduled monitoring jobs, and any environment without a display (servers, containers, GitHub Actions).

Use headed for: writing and debugging new tests (you need to see what's happening), exploratory testing, demos, and any situation where you need to visually verify behavior. The shift-left testing approach works well here: write tests in headed mode during development, then switch to headless for CI/CD pipeline.

The switch is one line. Remove --headless=new from your options and same test runs in a visible browser. This is one of headless Chrome's biggest advantages over old PhantomJS approach, where headless and headed were entirely different browsers. With modern Chrome, you're running exact same browser either way. Your test automation strategy should account for both modes: headless for speed in CI, headed for debugging when something fails.

FAQ

What is headless Chrome in Selenium?

Headless Chrome runs Chrome browser without a visible window. Selenium sends same commands and gets same results. Tests execute faster because Chrome skips rendering pixels to screen. The browser engine, JavaScript execution, and page rendering are identical to regular Chrome.

How do I run Selenium headless in Python?

Install Selenium (pip install selenium), create a ChromeOptions object, add --headless=new as an argument, and pass options to webdriver.Chrome(). Selenium Manager handles ChromeDriver installation automatically on Selenium 4.6+.

Why was setHeadless() removed from Selenium?

Selenium removed setHeadless() convenience method in version 4.10.0 because Chrome now has multiple headless modes (--headless=new vs old --headless). The method only supported old mode. Adding argument directly through add_argument("--headless=new") gives you control over which mode to use.

Do headless Chrome tests behave same as regular Chrome?

On Chrome 112+, yes. Both modes share same browser code. Before Chrome 112, headless mode was a separate implementation with its own bugs. If you see behavior differences, check your Chrome version, viewport size settings, and whether site detects automated browsers.

What flags do I need for headless Chrome in Docker?

Add --headless=new, --no-sandbox, --disable-dev-shm-usage, and --window-size=1920,1080. The sandbox and shared memory flags are specific to containerized environments. Without them, Chrome will crash or fail to start.

Is headless Chrome faster than regular Chrome?

Typically 10-30% faster for test execution because Chrome skips painting pixels to screen and GPU compositing. The actual page loading, JavaScript execution, and DOM operations take same time. The speed gain comes from skipping visual rendering step.

‍

About the Author:

Zaid Abdul Bari
Product, Drizz
Day-one Drizz PM who shipped Fathom and runs on an undefeated supply of Vietnamese iced coffee.
Schedule a demo