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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Install Selenium in a virtual environment, add Chrome’s --headless=new argument to a ChromeOptions object, start webdriver.Chrome(options=options), and always call driver.quit() in a finally block. Current Selenium releases normally use Selenium Manager to find or download a compatible driver, so a hard-coded ChromeDriver path is unnecessary for most supported machines.

What headless Chrome means

Headless Chrome runs the same browser engine without opening a visible window. Selenium still creates a WebDriver session, loads pages, executes JavaScript, waits for elements and can save screenshots or PDFs. The difference is that rendering happens in the background, which is useful on servers, CI workers, containers and desktop scripts where no display is available.

Headless mode is a Chrome command-line switch. In Python, you pass it through ChromeOptions; it is not a separate Selenium driver.

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

Prerequisites and installation

Use an isolated Python environment

Create a project directory and virtual environment so Selenium and its dependencies do not interfere with system packages:

python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell
.venvScriptsActivate.ps1

If your machine uses python3 rather than python, substitute that command. Activate the environment in every shell where you run the script.

Install or upgrade Selenium

python -m pip install -U selenium

Selenium’s Python documentation recommends this installation method and a virtual environment. Check the current Selenium package metadata when choosing a Python version, because supported versions change over time.

Install Chrome or Chromium

A compatible Chrome or Chromium browser must exist on the machine. Selenium can discover the normal installation automatically. If the browser is installed in a nonstandard location, set its binary path as shown later.

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

The minimal working script

Save this as headless_example.py:

from selenium import webdriver

options = webdriver.ChromeOptions()
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()

Run it with:

python headless_example.py

The expected output is the page title, usually Example Domain. No Chrome window appears. The fixed viewport is optional, but it prevents responsive breakpoints from changing between runs and gives screenshots a predictable size.

What each line does

  • webdriver.ChromeOptions() collects Chrome-specific startup settings.
  • --headless=new starts Chrome without a visible window using the current headless implementation.
  • --window-size=1920,1080 sets the layout viewport; choose dimensions that match your test or capture.
  • webdriver.Chrome(options=options) creates the browser session. When you have not supplied a driver, Selenium Manager normally resolves one automatically.
  • driver.get() navigates to a URL and waits for the navigation command to complete according to the page-load strategy.
  • The finally block runs even when navigation or assertions fail, ensuring the browser and driver processes are closed.

Waiting for real page state

Headless does not mean “instant.” JavaScript applications may render after the initial document load. Use an explicit wait for a condition that proves the page is ready instead of inserting a long arbitrary sleep:

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")
    heading = WebDriverWait(driver, 15).until(
        EC.visibility_of_element_located((By.TAG_NAME, "h1"))
    )
    print(heading.text)
finally:
    driver.quit()

Choose a selector that represents usable content, set a timeout appropriate for your environment, and let a timeout fail clearly. For a network-heavy application, you can also wait for a specific loading element to disappear or for a result container to become visible.

Useful Chrome options for automation

Viewport, scale and screenshots

Use --window-size=1280,800 or another explicit size when responsive CSS matters. Selenium’s screenshot methods capture the browser’s rendered viewport; full-page capture is not consistently provided by the WebDriver API, so long pages may require scrolling and stitching or a browser-specific approach.

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

Alternate Chrome or Chromium binary

If Selenium cannot find a browser installed outside the normal locations, point ChromeOptions at it:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.binary_location = "/opt/chromium/chrome"
options.add_argument("--headless=new")

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

Use the actual path on your operating system. A wrong or non-executable path produces a startup error; verify it independently before blaming Selenium.

Custom user data and other switches

Chrome accepts many command-line switches, but add only those required by your environment. A temporary profile is safer than reusing a personal profile when several jobs run concurrently. Browser switches that disable security features can change what you are testing and should not be added merely because a copied snippet includes them.

Driver management: automatic versus pinned

Approach Best fit Trade-off
Selenium Manager Normal local development, CI and supported online environments Least setup; Selenium resolves a driver and may download one when needed.
Manually managed ChromeDriver Offline, air-gapped, tightly pinned or centrally provisioned machines More control, but you must maintain the executable and browser compatibility.

Selenium project documentation describes Selenium Manager as the official driver manager shipped with Selenium releases as of version 4.6. It acts as a fallback when you have not supplied a driver. This is why the minimal script contains no download URL and no executable path.

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.

When you must provide a driver service

Use a Service object when your organization supplies a specific driver executable, needs a custom log file or requires service-level arguments:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

service = Service(
    executable_path="/usr/local/bin/chromedriver",
    log_output="chromedriver.log"
)
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")

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

Use a driver whose major version matches the installed Chrome major version. A mismatch commonly causes a session-not-created error. If you pin ChromeDriver, pin the browser image or package as well; updating only one side can break a previously working build.

Modern Selenium patterns—and obsolete ones to remove

  • Use options.add_argument("--headless=new"); do not rely on the removed options.headless = True convenience property.
  • Pass a Service object for an explicit driver; do not use the removed executable_path keyword in the webdriver.Chrome constructor.
  • Use find_element(By.ID, "name") and related methods. The old find_element_by_* methods were removed in Selenium 4.3.
  • Pass capabilities through supported options APIs rather than the removed desired_capabilities constructor keyword.

These changes explain why older tutorials can fail even when Chrome itself is installed correctly.

Common failures and precise fixes

“Unable to obtain driver” or Selenium Manager download errors

Confirm that the process has network access to obtain a driver, that the Selenium package is upgraded, and that a browser is installed. In restricted environments, provision a matching ChromeDriver yourself and pass it with Service. Inspect the underlying Selenium Manager message; it usually identifies whether discovery, download or permissions failed.

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

“SessionNotCreatedException: only supports Chrome version …”

Compare the major version printed by Chrome’s About page with the major version of the manually supplied ChromeDriver. Install a matching driver or let Selenium Manager select one. Do not try to solve a major-version mismatch by adding unrelated headless flags.

Chrome exits immediately in a server or container

Check the browser’s stderr and the host’s sandbox, shared-memory and display policies. Container requirements vary by image and operating system, so there is no universal list of flags. First verify that Chrome can start in that exact environment, then add only the documented options your platform requires. A missing executable permission, read-only profile directory or unavailable shared-memory mount can all look like a Selenium failure.

“Chrome binary not found”

Install Chrome/Chromium or set options.binary_location to the real executable. Paths copied from another operating system will not work.

The page is blank or content is missing

Wait for a meaningful element, check the current URL and title, and capture page source or a diagnostic screenshot before quitting. Verify that the target does not require authentication, a consent choice or interaction. Headless mode can expose timing and responsive-layout assumptions that were hidden by manual testing.

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

Elements are present but not clickable

Wait for clickability, scroll the element into view, and check for overlays or a different viewport breakpoint. A selector finding an element in the DOM does not prove that it is visible or unobstructed.

The script leaves Chrome processes behind

Keep browser creation and use inside a try/finally structure and call driver.quit(), not merely driver.close(). If a process still remains after a hard crash, clean up the worker according to your operating system’s process controls and investigate the original exception.

Reliability and performance practices

  • Reuse one driver for a related sequence of pages instead of starting a new browser for every URL, but isolate tests that depend on a clean profile.
  • Set explicit page-load and explicit-wait timeouts so a dead server cannot hold a worker forever.
  • Keep viewport, locale, timezone and user-agent assumptions explicit when results are compared in CI.
  • Log the URL, browser version, Selenium version and exception type for failed runs; do not log credentials or session cookies.
  • Run jobs in separate temporary profiles when parallel workers might write to the same Chrome profile.
  • Use a deterministic browser image for release tests. Automatic driver resolution is convenient, while fully pinned browser and driver versions provide stronger reproducibility.
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 website screenshot rather than interactive browser testing, ScreenshotNeo provides a single HTTP request and an MCP server for AI agents. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

Here is the cURL request (the API documentation lists all options):

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same call in 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)

And in 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()));

ScreenshotNeo also offers full-page and element captures, dark mode, device presets, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone and geolocation controls, transparent backgrounds, resizing, selectable caching TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

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

FAQ

Can I run headless Chrome without Selenium?

Yes, Chrome can be controlled through other automation protocols and libraries, but this guide uses Selenium’s WebDriver API because it provides the Python bindings and lifecycle shown above.

Does headless mode change the website?

It uses Chrome’s rendering engine, but viewport, timing, permissions and server-side bot detection can differ from a headed desktop session. Validate the exact environment in which your automation will run.

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.

Should I use Chrome or Chromium?

Either can work when Selenium can launch the installed binary and a compatible driver is available. Chromium package names and installation paths vary by operating system.

How do I preserve cookies between runs?

Use a deliberate profile strategy or export and restore cookies through Selenium. Avoid sharing a writable profile among parallel jobs, and never place authentication cookies in source control or logs.

Frequently Asked Questions

Can headless Chrome display a window for debugging?

Temporarily remove the --headless=new argument, run the same script with a visible desktop session, and inspect the page interactively. Restore headless mode for the unattended run.

What should I record when a CI run fails only intermittently?

Record the browser and Selenium versions, URL, viewport, elapsed time, exception, current URL and a diagnostic screenshot or page source captured before teardown.

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.