skip to content

In Selenium 4, how do you run Chrome headless now that ChromeOptions.setHeadless() no longer exists?

level: middleimportance: should knowfreq 68%

answer

  1. A browser switch, not a Selenium toggle
  2. The old boolean convenience was removed
  3. It travels through the options argument list
  4. Chrome and Edge want --headless=new
  5. Firefox spells it with one dash

basics

~10 s

Headless is a browser switch, not a Selenium setting. Build a ChromeOptions, call addArguments with --headless=new, and pass that object to the ChromeDriver constructor. Selenium 4 removed the setHeadless convenience toggle.

solid answer

~40 s

Selenium 4 has no headless toggle any more — `setHeadless` was deprecated and then removed during the 4.x line — so headless is switched on with the browser's own launch argument. For Chrome and Edge you build a `ChromeOptions` or `EdgeOptions`, call `addArguments("--headless=new")` and hand the object to the driver constructor; the `=new` suffix names Chrome's modern headless implementation rather than the older separate one. Firefox spells it with a single dash: `addArguments("-headless")`. Safari has no headless mode at all, so `SafariOptions` offers no equivalent. Nothing about this is Selenium's: `addArguments` only appends opaque strings to the browser's command line, so a misspelled switch is generally ignored by the browser rather than rejected by the client, and the symptom is a window that still opens.

code

java · 18 lines
java
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class HeadlessRecipeCollectionTest {
  public static void main(String[] args) {
    ChromeOptions options = new ChromeOptions();
    options.addArguments("--headless=new");

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

go deeper

for a junior

Be ready to say that headless is turned on with a browser launch argument added through the options object, and to name --headless=new for Chrome. Knowing there is no headless boolean any more is enough at this stage.

for a middle

Explain that addArguments only appends opaque strings to the browser's command line, so nothing validates them, and give Firefox's single-dash -headless alongside Chrome's --headless=new rather than assuming one spelling.

for a senior

An interviewer expects you to have migrated a real suite off setHeadless and to explain why a boolean could not survive Chrome growing two headless implementations. Be ready to say what headless does not change: the WebDriver command set.

for a principal

Own the consequence for a browser matrix. Safari has no headless mode, so promising every lane runs windowless promises something the options objects cannot deliver, and the matrix has to be decided with that in mind.

## What headless actually is **Headless** means the browser paints and runs the page without opening a visible window. It is a **browser** capability, not a Selenium one: nothing in the WebDriver command set hides a window. What Selenium gives you is the typed **options object** — `ChromeOptions`, `FirefoxOptions`, `EdgeOptions`, `SafariOptions` — and its `addArguments` list, which the driver appends to the command line it uses to start the browser process. So the answer always has the same shape: put the browser's own launch switch into the options object, then hand that object to the driver. ## The spelling, per browser | Browser | Options class | Headless switch | Note | |---|---|---|---| | Chrome | `ChromeOptions` | `--headless=new` | two dashes; `=new` names the modern implementation | | Edge | `EdgeOptions` | `--headless=new` | shares Chrome's `ChromiumOptions` base, so the same switch | | Firefox | `FirefoxOptions` | `-headless` | one dash — Gecko's own command-line convention | | Safari | `SafariOptions` | none | Safari has no headless mode, and the class has no argument list | The `=new` suffix is worth being explicit about. Chrome carried two headless implementations for a stretch of releases: the original one, effectively a separate mode with its own quirks, and the newer one that is the ordinary browser with no window on screen. Writing `--headless=new` states which you mean regardless of which Chrome build a given machine happens to have installed. ## Why `setHeadless()` is gone Early Selenium 4 shipped a convenience toggle, `ChromeOptions.setHeadless(boolean)`, with the same method on `FirefoxOptions`. It was deprecated and then removed during the Selenium 4 line, and against a current Selenium 4 client the call no longer compiles. The reason is the table above: once "headless" stopped being one unambiguous flag, a boolean could no longer express the request, and the client chose not to guess on your behalf. Migrating a suite off it is mechanical: 1. Delete the `setHeadless(true)` call. 2. Add `options.addArguments("--headless=new")` for Chrome or Edge, or `options.addArguments("-headless")` for Firefox. 3. Leave everything else alone — locators, waits and assertions are untouched, because headless changes no WebDriver command. ## Where the switch travels The options object is a client-side builder. It reaches the driver when you pass it to the driver constructor as part of asking for a new session, and the driver process turns the argument list into the command line of the browser it starts. That is why an argument only ever takes effect at launch: mutating an options object after the session exists changes nothing, and there is no command that adds a switch to a running browser. ## Putting it in a run A suite for a **recipe collection manager** opens the "weeknight dinners" collection and counts the cards on it. Headless or not, the script is identical; only the construction of the options object differs. ```java ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); WebDriver driver = new ChromeDriver(options); ``` Two properties follow from `addArguments` being a plain list of strings: - **Selenium does not validate the switch.** The list stores opaque text, so a misspelled `--headles=new` is passed straight to Chrome, generally ignored there, and shows up as a visible window rather than as an exception. - **One switch per string.** `addArguments("--headless=new", "--incognito")` adds two switches; `addArguments("--headless=new --incognito")` adds one meaningless one. ## What still bites people - Building the options object and never passing it to the constructor, so the session starts with nothing applied. - Believing headless is a different browser. It is the same binary, the same engine and the same command set; only the window is missing. - Expecting Safari to follow. `SafariOptions` has nothing to set, so a lane that must run windowless cannot be a Safari lane. - Reaching for a screenshot as proof the flag arrived. A headless screenshot looks like a headed one; the evidence is the absent window and the browser's own command line. ## The other typed setters, for context `addArguments` is the escape hatch onto the browser's command line, but the options classes also carry setters that are not arguments at all: `setBinary(...)` names which browser executable to launch when several builds are installed, `setExperimentalOption("prefs", ...)` on the Chromium classes and `addPreference(...)` on `FirefoxOptions` write profile settings, and `setPageLoadStrategy(...)` sets a session-level strategy. Headless is deliberately not among them, because it belongs to the browser — so it travels as a browser switch.

  • Your suite passes --headless without the =new suffix. What are you actually asking Chrome for?
    You are letting the browser decide. Chrome shipped two headless implementations for a stretch of releases, and plain `--headless` selected different ones depending on the build installed. Writing `--headless=new` pins the meaning, so the same suite behaves the same way across the Chrome versions your machines actually have.
  • Does addArguments validate the switch you hand it?
    No. It stores opaque strings and appends them to the browser's command line, so Selenium never inspects them. An unrecognised or misspelled switch is generally ignored by the browser, which means a typo shows up as behaviour — a window that still opens — rather than as an exception at construction time.
  • Can a running session be switched into headless mode after the browser has started?
    No. Launch arguments are part of the browser process's command line and are fixed when it starts, and there is no WebDriver command that hides a window. Mutating the options object afterwards changes nothing. The only route is quitting the session and starting a new one from a new options object.

saying these in an interview costs you the question

  • Says setHeadless() is still how you enable headless in Selenium 4
  • Treats headless as a Selenium setting rather than a browser launch switch
  • Passes several switches as one string inside a single addArguments argument
  • Believes Safari can be driven headless with the right SafariOptions call
  • Expects Selenium to throw when a browser switch is misspelled