How to Run Chrome in Headless Mode
Chrome's headless mode uses the exact same rendering engine and JavaScript runtime as the full desktop browser. Since Chrome 112, the "new" headless mode replaced the older separate implementation, ensuring that headless Chrome behaves identically to headed Chrome in all rendering and JavaScript execution scenarios. This guide covers every practical method for launching Chrome headless, configuring it for different environments, and verifying that it works correctly.
Step 1: Run Chrome Headless from the Command Line
The simplest way to run Chrome in headless mode is directly from the terminal using the --headless=new flag. This approach does not require any programming language or framework, just Chrome installed on your system.
On Linux, the command looks like: google-chrome --headless=new --disable-gpu --screenshot=output.png https://example.com. On macOS, replace the binary name with the full application path: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless=new --screenshot=output.png https://example.com. On Windows, use the Chrome executable path from Program Files.
The --headless=new flag activates the modern headless mode. The --disable-gpu flag is recommended on Linux systems where GPU acceleration may not be available in headless environments. The --screenshot flag tells Chrome to capture a screenshot of the page and save it to the specified file. You can also use --print-to-pdf to generate a PDF document instead.
Additional useful command-line flags include --window-size=1920,1080 to set the viewport dimensions, --no-sandbox for running in Docker containers (where the sandbox is unnecessary because Docker provides its own isolation), --user-data-dir=/tmp/chrome-profile to specify a custom profile directory, and --dump-dom to print the rendered DOM to stdout instead of capturing a screenshot. The --virtual-time-budget flag lets you advance page timers instantly, useful for pages that have loading animations or delays.
Step 2: Run Chrome Headless with Playwright
Playwright launches Chromium in headless mode by default, requiring no special configuration. When you call the chromium.launch() method, Playwright downloads and manages its own Chromium binary, starts it in headless mode, and provides a high-level API for controlling it.
In JavaScript: const browser = await chromium.launch(); launches headless Chromium. To run in headed mode for debugging, pass { headless: false }. In Python: browser = playwright.chromium.launch() does the same thing. Playwright abstracts away all the command-line flags, handling them internally based on your configuration.
Playwright's headless implementation includes several optimizations beyond the basic --headless flag. It configures the viewport, disables unnecessary browser features that slow down automation, and sets up the DevTools Protocol connection automatically. The browser object you receive has methods for creating pages, contexts, and handling browser-level events like disconnections and crashes.
For CI/CD environments, Playwright provides the npx playwright install --with-deps chromium command that installs both the browser binary and all required system dependencies (fonts, graphics libraries, audio codecs) in one step. This is particularly important on Linux servers where missing shared libraries are a common cause of headless Chrome startup failures.
Step 3: Run Chrome Headless with Puppeteer
Puppeteer, like Playwright, runs Chrome in headless mode by default. The launch command const browser = await puppeteer.launch(); downloads Chromium (if not already present) and starts it headless. To see the browser window during development, use { headless: false }.
Puppeteer also supports a "shell" headless mode through { headless: 'shell' }, which uses the older headless implementation. This mode starts faster and uses less memory, but it may behave differently from the full browser in edge cases involving certain rendering features or JavaScript API behavior. The default { headless: true } uses the new headless mode that matches headed Chrome exactly.
You can pass custom Chrome arguments through the args option: puppeteer.launch({ args: ['--no-sandbox', '--window-size=1920,1080'] }). The executablePath option lets you point Puppeteer at a specific Chrome or Chromium binary instead of using the bundled download, useful when you need to match a particular Chrome version or use a system-installed browser for consistency across environments.
Step 4: Run Chrome Headless with Selenium
Selenium requires explicit configuration to run Chrome in headless mode. You create a ChromeOptions object, add the headless argument, and pass it to the Chrome WebDriver constructor.
In Python: create options with options = webdriver.ChromeOptions(), add the headless flag with options.add_argument('--headless=new'), and launch with driver = webdriver.Chrome(options=options). In Java, the equivalent uses ChromeOptions options = new ChromeOptions(); options.addArguments("--headless=new"); followed by WebDriver driver = new ChromeDriver(options);.
Selenium 4+ includes Selenium Manager, which automatically downloads and configures ChromeDriver to match your installed Chrome version. Earlier versions required manual ChromeDriver management, which was a frequent source of version mismatch errors when Chrome auto-updated. If you need to pin a specific ChromeDriver version, you can still download it manually and specify the path through the service parameter.
Additional Chrome options commonly used with Selenium include --disable-extensions to prevent extensions from loading, --disable-dev-shm-usage to avoid shared memory issues in Docker containers, and --remote-debugging-port=9222 to enable CDP access alongside the WebDriver session for advanced debugging.
Step 5: Configure the Headless Environment
Headless Chrome behaves identically to headed Chrome in most respects, but the environment it runs in can affect its behavior. Viewport size matters because responsive websites change their layout based on screen dimensions. If you do not set a viewport size, Chrome headless defaults to 800x600, which may trigger mobile or tablet layouts on responsive sites. Set the viewport explicitly: 1920x1080 for desktop testing, 375x812 for iPhone simulation, or 768x1024 for tablet views.
Font availability affects text rendering and layout measurements. Servers and containers often lack the fonts installed on desktop systems. Missing fonts cause text to render with fallback typefaces, which can change element dimensions and alter page layout in subtle ways. Install standard font packages (fonts-liberation, fonts-noto-cjk on Linux) or use Playwright's --with-deps flag to handle this automatically.
Timezone and locale settings can affect page content and behavior. Set the TZ environment variable to control the timezone Chrome uses internally, and configure language preferences through the --lang flag or through browser context settings in Playwright. These details matter when testing localized content, dealing with date formatting, or scraping sites that vary content by geographic region.
Step 6: Verify Headless Mode Is Working
The simplest verification is to capture a screenshot and confirm the page rendered correctly. In Playwright: await page.screenshot({ path: 'verify.png' }). In Puppeteer: await page.screenshot({ path: 'verify.png' }). In Selenium: driver.save_screenshot('verify.png'). Open the resulting image and confirm it shows the expected page content rather than a blank screen or error page.
You can also check the user agent string by evaluating navigator.userAgent in the browser context. Modern Chrome headless uses the same user agent as headed Chrome, but older headless implementations included "HeadlessChrome" in the user agent string. Verify that your version does not leak the headless indicator if detection avoidance matters for your use case.
For programmatic verification in automated pipelines, evaluate JavaScript in the page context to check for expected elements, content, or state. This confirms not just that Chrome started successfully, but that the page loaded and rendered correctly in your specific headless environment with its particular combination of fonts, viewport settings, and system libraries.
Chrome runs in headless mode with the --headless=new flag (command line), or by default when launched through Playwright or Puppeteer. Selenium requires explicitly adding the headless argument to ChromeOptions. For production use, configure viewport size, install fonts, and set timezone to match your target environment.