Skip to main content
SeleniumDecoded

Headless Browsers

Run Selenium tests without a visible browser UI for faster execution in CI/CD pipelines.

Selenium 3 & 4 Stable

Headless browsers run without a graphical user interface, making them ideal for CI/CD pipelines and server environments where no display is available.

Benefits of Headless Mode

AspectHeadedHeadless
SpeedBaseline20-30% faster
MemoryHigherLower
CI/CDNeeds displayWorks anywhere
DebuggingEasy visualRequires screenshots

Chrome Headless

Chrome Headless Configuration
Selenium 3 & 4 Stable
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"); // New headless mode (Chrome 109+)
options.addArguments("--window-size=1920,1080");
options.addArguments("--disable-gpu"); // Recommended for Windows
options.addArguments("--no-sandbox"); // Required for Docker/CI
options.addArguments("--disable-dev-shm-usage"); // Overcome limited resources
WebDriver driver = new ChromeDriver(options);
// Verify headless mode
System.out.println("Running headless: " +
driver.manage().getCookieNamed("headless")); // Will be null but page works
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new") # New headless mode (Chrome 109+)
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu") # Recommended for Windows
options.add_argument("--no-sandbox") # Required for Docker/CI
options.add_argument("--disable-dev-shm-usage") # Overcome limited resources
driver = webdriver.Chrome(options=options)
# Run your tests
driver.get("https://example.com")
print(f"Title: {driver.title}")
driver.quit()
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const options = new chrome.Options();
options.addArguments('--headless=new'); // New headless mode
options.addArguments('--window-size=1920,1080');
options.addArguments('--disable-gpu');
options.addArguments('--no-sandbox');
options.addArguments('--disable-dev-shm-usage');
const driver = await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
// Run your tests
await driver.get('https://example.com');
console.log('Title:', await driver.getTitle());
await driver.quit();
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
var options = new ChromeOptions();
options.AddArgument("--headless=new"); // New headless mode
options.AddArgument("--window-size=1920,1080");
options.AddArgument("--disable-gpu");
options.AddArgument("--no-sandbox");
options.AddArgument("--disable-dev-shm-usage");
IWebDriver driver = new ChromeDriver(options);
// Run your tests
driver.Navigate().GoToUrl("https://example.com");
Console.WriteLine($"Title: {driver.Title}");
driver.Quit();

Firefox Headless

Firefox Headless Configuration
Selenium 3 & 4 Stable
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
FirefoxOptions options = new FirefoxOptions();
options.addArguments("--headless");
options.addArguments("--width=1920");
options.addArguments("--height=1080");
WebDriver driver = new FirefoxDriver(options);
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
options = Options()
options.add_argument("--headless")
options.add_argument("--width=1920")
options.add_argument("--height=1080")
driver = webdriver.Firefox(options=options)
const { Builder } = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');
const options = new firefox.Options();
options.addArguments('--headless');
options.addArguments('--width=1920');
options.addArguments('--height=1080');
const driver = await new Builder()
.forBrowser('firefox')
.setFirefoxOptions(options)
.build();
using OpenQA.Selenium.Firefox;
var options = new FirefoxOptions();
options.AddArgument("--headless");
options.AddArgument("--width=1920");
options.AddArgument("--height=1080");
IWebDriver driver = new FirefoxDriver(options);

Edge Headless

Edge Headless Configuration
Selenium 3 & 4 Stable
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
EdgeOptions options = new EdgeOptions();
options.addArguments("--headless=new");
options.addArguments("--window-size=1920,1080");
WebDriver driver = new EdgeDriver(options);
from selenium import webdriver
from selenium.webdriver.edge.options import Options
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Edge(options=options)
const { Builder } = require('selenium-webdriver');
const edge = require('selenium-webdriver/edge');
const options = new edge.Options();
options.addArguments('--headless=new');
options.addArguments('--window-size=1920,1080');
const driver = await new Builder()
.forBrowser('MicrosoftEdge')
.setEdgeOptions(options)
.build();
using OpenQA.Selenium.Edge;
var options = new EdgeOptions();
options.AddArgument("--headless=new");
options.AddArgument("--window-size=1920,1080");
IWebDriver driver = new EdgeDriver(options);

Environment-Based Configuration

Toggle Headless via Environment
Selenium 3 & 4 Stable
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class DriverFactory {
public static WebDriver createDriver() {
ChromeOptions options = new ChromeOptions();
// Check environment variable or system property
boolean headless = Boolean.parseBoolean(
System.getenv("HEADLESS") != null ?
System.getenv("HEADLESS") :
System.getProperty("headless", "false")
);
if (headless) {
options.addArguments("--headless=new");
options.addArguments("--window-size=1920,1080");
options.addArguments("--disable-gpu");
options.addArguments("--no-sandbox");
}
return new ChromeDriver(options);
}
}
// Run headless: java -Dheadless=true ...
// Or: HEADLESS=true java ...
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def create_driver():
options = Options()
# Check environment variable
headless = os.getenv("HEADLESS", "false").lower() == "true"
if headless:
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
return webdriver.Chrome(options=options)
# Run headless: HEADLESS=true pytest
const { Builder } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
async function createDriver() {
const options = new chrome.Options();
// Check environment variable
const headless = process.env.HEADLESS === 'true';
if (headless) {
options.addArguments('--headless=new');
options.addArguments('--window-size=1920,1080');
options.addArguments('--disable-gpu');
options.addArguments('--no-sandbox');
}
return await new Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
}
// Run headless: HEADLESS=true npm test
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
public class DriverFactory
{
public static IWebDriver CreateDriver()
{
var options = new ChromeOptions();
// Check environment variable
bool headless = Environment.GetEnvironmentVariable("HEADLESS") == "true";
if (headless)
{
options.AddArgument("--headless=new");
options.AddArgument("--window-size=1920,1080");
options.AddArgument("--disable-gpu");
options.AddArgument("--no-sandbox");
}
return new ChromeDriver(options);
}
}

Debugging Headless Tests

Take Screenshots

Screenshot for Debugging
Selenium 3 & 4 Stable
// Take screenshot when something fails
try {
WebElement element = driver.findElement(By.id("missing"));
} catch (NoSuchElementException e) {
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(screenshot.toPath(), new File("debug_screenshot.png").toPath());
throw e;
}
# Take screenshot when something fails
try:
element = driver.find_element(By.ID, "missing")
except NoSuchElementException:
driver.save_screenshot("debug_screenshot.png")
raise
// Take screenshot when something fails
try {
await driver.findElement(By.id('missing'));
} catch (e) {
const screenshot = await driver.takeScreenshot();
require('fs').writeFileSync('debug_screenshot.png', screenshot, 'base64');
throw e;
}
// Take screenshot when something fails
try
{
driver.FindElement(By.Id("missing"));
}
catch (NoSuchElementException)
{
Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile("debug_screenshot.png");
throw;
}

Save Page Source

# Save HTML for debugging
try:
element = driver.find_element(By.ID, "missing")
except:
with open("debug_page.html", "w") as f:
f.write(driver.page_source)
raise

CI/CD Configuration

GitHub Actions

name: Selenium Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: pip install selenium pytest
- name: Run tests (headless)
env:
HEADLESS: 'true'
run: pytest tests/ -v
- name: Upload screenshots on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: screenshots/

Docker

FROM python:3.11-slim
# Install Chrome
RUN apt-get update && apt-get install -y \
wget \
gnupg \
&& wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
&& apt-get update \
&& apt-get install -y google-chrome-stable \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy tests
COPY . /app
WORKDIR /app
# Run tests in headless mode
ENV HEADLESS=true
CMD ["pytest", "tests/", "-v"]

Headless Limitations

FeatureHeadedHeadless
Visual debuggingYesScreenshots only
File downloadsEasyNeeds configuration
Browser extensionsYesLimited
Drag and dropWorksMay need workarounds
Video captureEasyRequires tools

Best Practices

  1. Always set window size - Headless has no default viewport
  2. Use screenshots for debugging failed tests
  3. Test both modes - Some bugs only appear in headed mode
  4. Configure CI properly - Include all necessary flags
  5. Monitor resource usage - Headless uses less but not zero

Next Steps