ScreenshotNeo

BlogHow-to

How to Run Chrome in Headless Mode in Selenium Java

Run Chrome without a visible UI in Selenium Java with ChromeOptions, reliable CI settings, troubleshooting, and a ScreenshotNeo alternative.

By the ScreenshotNeo team1 October 20267 min read

Use Selenium Java’s ChromeOptions and pass it to ChromeDriver:

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");

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

The --headless=new argument selects Chrome’s newer unified headless implementation. Chrome Headless runs without a visible UI, while Selenium still controls a normal browser session. See the Selenium Chrome documentation and Chrome Headless documentation.

What headless mode does

Headless mode starts Chrome without displaying a window. Your test can still navigate, find elements, execute JavaScript, download files, take screenshots and read page state. Since Chrome 112, the unified implementation follows the normal Chrome browser code path while keeping the UI hidden. From Chrome 132.0.6793.0 onward, the older implementation is also available as a separate chrome-headless-shell binary.

Prerequisites

  • Java 8 or later (use the version required by your Selenium release).
  • Selenium 4 added to your project.
  • A Chrome or Chromium installation available to the process.
  • A compatible ChromeDriver. Selenium Manager can obtain a driver when one is not already supplied.

Selenium’s Chrome support is compatible with Chrome 75 and newer, and ChromeDriver’s major version should match Chrome’s major version.

Maven dependency

<dependency>
  <groupId>org.seleniumhq.selenium</groupId>
  <artifactId>selenium-java</artifactId>
  <version>4.XX.X</version>
</dependency>

Replace 4.XX.X with the Selenium 4 version selected by your project. Keep Selenium dependencies consistent across modules.

Complete Java example with a deterministic viewport

import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class HeadlessChrome {
  public static void main(String[] args) {
    ChromeOptions options = new ChromeOptions();
    options.addArguments("--headless=new");
    options.addArguments("--window-size=1920,1080");

    WebDriver driver = new ChromeDriver(options);
    try {
      driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));
      driver.get("https://example.com");
      System.out.println("Title: " + driver.getTitle());
      System.out.println("URL: " + driver.getCurrentUrl());
    } finally {
      driver.quit();
    }
  }
}

A fixed window size makes responsive breakpoints and screenshots more predictable. Choose dimensions that match the layout you are testing.

Choosing --headless=new or --headless

Argument When to use it Notes
--headless=new Current Chromium-based Chrome Preferred for Selenium 4 projects targeting the unified implementation.
--headless Compatibility with an environment that specifically expects the general headless flag Verify the Chrome version and rendering behavior used by your CI image.

Selenium deprecated its convenience headless method in 4.8.0 and removed it in 4.10.0. Configure the mode with Chrome arguments instead of relying on setHeadless(true).

Useful ChromeOptions settings

Setting Example Purpose and cautions
Headless mode --headless=new Runs without a visible UI.
Viewport --window-size=1920,1080 Controls responsive layout and screenshot dimensions.
Isolated profile --user-data-dir=/tmp/chrome-job-123 Use a separate directory for parallel jobs or controlled browser state. Ensure each concurrent session has its own directory.
Container sandbox --no-sandbox Add only when the container runtime requires it after investigating sandbox permissions. It is not a universal Selenium setting.
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");
options.addArguments("--window-size=1366,768");
options.addArguments("--user-data-dir=/tmp/selenium-profile-42");
// Add --no-sandbox only if your container requires it.

Remote WebDriver and CI

ChromeOptions also carries Chrome-specific capabilities when you connect to a remote Selenium server:

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

WebDriver driver = new org.openqa.selenium.remote.RemoteWebDriver(
    java.net.URI.create("http://selenium-grid:4444").toURL(),
    options
);

The remote node, rather than your Java process, must have Chrome installed. Keep the browser and driver versions aligned on that node.

Waiting for real page state

Headless mode does not change how asynchronous pages load. Use explicit waits for the state your test needs instead of assuming that get() means every element is ready.

import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
driver.get("https://example.com/dashboard");
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("main")));

Taking a screenshot in Java

import java.nio.file.Files;
import java.nio.file.Path;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;

byte[] png = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
Files.write(Path.of("page.png"), png);

For a full-page image, Selenium’s built-in screenshot behavior depends on the driver and browser version. If you need consistent full-page capture, an API can avoid maintaining browser infrastructure.

Or skip the browser setup

ScreenshotNeo returns a PNG, JPEG, WebP or PDF from one GET request. Its capture pipeline accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before the shot. Bot checks, 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. It also provides an MCP server for Claude, Cursor and other MCP clients, with take_screenshot, get_page_info and capture_pdf tools.

See the ScreenshotNeo API documentation for all options.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

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)

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}`);

ScreenshotNeo includes full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API and an OpenAPI specification.

There are 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.

Troubleshooting

SessionNotCreatedException or ChromeDriver will not start

Cause: Chrome and ChromeDriver major versions do not match, or Chrome is missing on the machine running the driver.

Fix: Check both versions, update the driver or browser together, and confirm the executable is available to the Selenium process. Selenium Manager can provide a driver when your environment permits it.

setHeadless(true) does not compile

Cause: The Selenium 4 convenience API was deprecated in 4.8.0 and removed in 4.10.0.

Fix: Create ChromeOptions, add --headless=new (or the mode required by your target Chrome), and pass the options to ChromeDriver.

The page looks different in CI

Cause: Different viewport dimensions, fonts, device scale or responsive breakpoints.

Fix: Set --window-size, use the same Chrome version as local development, and wait for the page state your assertion needs.

Chrome exits immediately in a container

Cause: Sandbox permissions, shared-memory limits, missing libraries or an invalid profile directory.

Fix: Inspect the container logs and Chrome error output first. Provide a writable, isolated --user-data-dir. Add --no-sandbox only when the container’s security model specifically requires it.

Parallel tests interfere with one another

Cause: Sessions share a profile or another mutable resource.

Fix: Give every job a unique profile directory and ensure each test closes its driver with quit().

The Java process hangs after a test

Cause: Chrome or the driver service is still running.

Fix: Put driver.quit() in a finally block, including when navigation or assertions throw.

Performance, reliability and cost considerations

  • Startup: Creating a browser is expensive compared with reusing a controlled session, but reuse requires careful cleanup of cookies, storage and profiles.
  • Determinism: Pin the browser image, driver major version, viewport and test data in CI.
  • Concurrency: Limit parallel Chrome processes to the CPU and memory available to the runner. Isolate profiles.
  • Waiting: Prefer explicit, condition-based waits over long fixed sleeps.
  • Cleanup: Always call quit(); leaked browser processes eventually make a worker unreliable.
  • Headless versus speed: The supplied sources do not establish a universal performance advantage. Measure your own pages and CI image if runtime matters.
  • Hosted capture costs: ScreenshotNeo bills only clean shots. Bot checks, blank pages, timeouts, failed loads and cache hits are free, with the verdict and billing state returned in headers.

Checklist

  • Use Selenium 4 and ChromeOptions.
  • Prefer --headless=new for current Chrome.
  • Match Chrome and ChromeDriver major versions.
  • Set a deliberate window size for layout-sensitive tests.
  • Use explicit waits for asynchronous content.
  • Give parallel jobs isolated profiles.
  • Add container-specific flags only after diagnosing the environment.
  • Call driver.quit() in finally.

FAQ

Does headless Chrome run a different browser?

Current unified headless Chrome shares the normal browser code path while hiding the UI. The older implementation is a separate shell binary in Chrome versions starting with 132.0.6793.0.

Can I use extensions in headless mode?

Extension behavior depends on the Chrome version and selected headless implementation. Verify the exact Chrome and Selenium combination in your CI image rather than assuming all extensions behave identically.

Do I need to install ChromeDriver manually?

Not always. Selenium Manager can obtain a suitable driver when it is not already available, but the browser itself still needs to be present and reachable.

Which option should I use for screenshots?

Start with --headless=new and a fixed --window-size. For hosted screenshots without browser maintenance, use ScreenshotNeo’s API or MCP server.