How to run a headless browser in Selenium

CI runners and remote test hosts often need browser coverage without a desktop session. Running Chrome headless through Selenium keeps the normal WebDriver command flow while the browser renders pages off screen, so a test can open a page, locate elements, and assert content from a terminal job.

In Python, headless mode belongs in ChromeOptions before the webdriver.Chrome() session starts. The --headless=new argument selects the current Chrome headless implementation, and --window-size gives responsive layouts, screenshots, and element positions a repeatable starting size.

The browser and driver still have to match before the session can start. Selenium Manager can resolve ChromeDriver when no driver is supplied, while locked-down CI images can pass an explicit service path. Headless runs can still differ from visible browser runs around viewport size, fonts, GPU behavior, downloads, and sandbox limits, so keep one visible-browser debug path for failures that reproduce only in automation.

Steps to run a headless browser in Selenium:

  1. Create selenium-headless-browser-run.py with a local page and ChromeOptions headless settings.
    selenium-headless-browser-run.py
    from pathlib import Path
    from tempfile import TemporaryDirectory
    import os
    import shutil
     
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
     
     
    HTML = """<!doctype html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Selenium headless demo</title>
      </head>
      <body>
        <h1>Headless browser ready</h1>
      </body>
    </html>
    """
     
     
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,720")
     
    for browser_name in ("google-chrome", "chromium", "chromium-browser"):
        browser_path = shutil.which(browser_name)
        if browser_path:
            options.binary_location = browser_path
            break
     
    if hasattr(os, "geteuid") and os.geteuid() == 0:
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-dev-shm-usage")
     
    driver_path = shutil.which("chromedriver")
    service = Service(driver_path) if driver_path else Service()
     
    with TemporaryDirectory() as tmpdir:
        page = Path(tmpdir) / "headless-demo.html"
        page.write_text(HTML, encoding="utf-8")
     
        driver = webdriver.Chrome(service=service, options=options)
        try:
            driver.get(page.as_uri())
            window_size = driver.get_window_size()
            print(f"title: {driver.title}")
            print(f"heading: {driver.find_element(By.TAG_NAME, 'h1').text}")
            print(f"window_size: {window_size['width']}x{window_size['height']}")
            print(f"browser: {driver.capabilities.get('browserName')}")
        finally:
            driver.quit()

    The script uses a temporary local page, so the smoke test does not depend on external network access. The root-only flags are for disposable containers and CI jobs that run Chrome as root; omit them on normal workstations.

  2. Run the script and confirm that Selenium reads the hidden page.
    $ python3 selenium-headless-browser-run.py
    title: Selenium headless demo
    heading: Headless browser ready
    window_size: 1280x720
    browser: chrome
  3. Move the options block into the project driver fixture.
    options = Options()
    options.add_argument("--headless=new")
    options.add_argument("--window-size=1280,720")
    driver = webdriver.Chrome(options=options)

    Set options.binary_location and a Service path only when the runner uses a pinned browser or cannot use Selenium Manager.
    Related: How to configure ChromeDriver for Selenium

  4. Keep a page assertion after navigation.
    driver.get("https://app.example.com/login")
    assert driver.title == "Sign in"
    assert driver.find_element(By.TAG_NAME, "h1").text == "Sign in"

    A started WebDriver session only proves the browser launched. A title or element assertion proves the headless browser rendered the target page.

  5. Remove the temporary smoke-test script.
    $ rm selenium-headless-browser-run.py