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
| Aspect | Headed | Headless |
|---|---|---|
| Speed | Baseline | 20-30% faster |
| Memory | Higher | Lower |
| CI/CD | Needs display | Works anywhere |
| Debugging | Easy visual | Requires 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 Windowsoptions.addArguments("--no-sandbox"); // Required for Docker/CIoptions.addArguments("--disable-dev-shm-usage"); // Overcome limited resources
WebDriver driver = new ChromeDriver(options);
// Verify headless modeSystem.out.println("Running headless: " + driver.manage().getCookieNamed("headless")); // Will be null but page worksfrom selenium import webdriverfrom 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 Windowsoptions.add_argument("--no-sandbox") # Required for Docker/CIoptions.add_argument("--disable-dev-shm-usage") # Overcome limited resources
driver = webdriver.Chrome(options=options)
# Run your testsdriver.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 modeoptions.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 testsawait 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 modeoptions.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 testsdriver.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 webdriverfrom 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 webdriverfrom 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 osfrom selenium import webdriverfrom 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 pytestconst { 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 testusing 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 failstry { 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 failstry: element = driver.find_element(By.ID, "missing")except NoSuchElementException: driver.save_screenshot("debug_screenshot.png") raise// Take screenshot when something failstry { 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 failstry{ driver.FindElement(By.Id("missing"));}catch (NoSuchElementException){ Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot(); screenshot.SaveAsFile("debug_screenshot.png"); throw;}Save Page Source
# Save HTML for debuggingtry: element = driver.find_element(By.ID, "missing")except: with open("debug_page.html", "w") as f: f.write(driver.page_source) raiseCI/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 ChromeRUN 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 dependenciesCOPY requirements.txt .RUN pip install -r requirements.txt
# Copy testsCOPY . /appWORKDIR /app
# Run tests in headless modeENV HEADLESS=trueCMD ["pytest", "tests/", "-v"]Headless Limitations
| Feature | Headed | Headless |
|---|---|---|
| Visual debugging | Yes | Screenshots only |
| File downloads | Easy | Needs configuration |
| Browser extensions | Yes | Limited |
| Drag and drop | Works | May need workarounds |
| Video capture | Easy | Requires tools |
Best Practices
- Always set window size - Headless has no default viewport
- Use screenshots for debugging failed tests
- Test both modes - Some bugs only appear in headed mode
- Configure CI properly - Include all necessary flags
- Monitor resource usage - Headless uses less but not zero
Next Steps
- Parallel Execution - Combine with headless
- Selenium Grid - Distributed headless
- Browser Options - Configure browser settings
- Screenshots and Videos - Capture test evidence
- Debugging Tips - Debug without UI