ScreenshotNeo

BlogHow-to

How to Run Playwright in Headless Mode

Run Playwright without opening a browser: install browsers, configure headless tests, debug CI failures, and choose the right Chromium mode.

By the ScreenshotNeo team1 October 20267 min read

Use npx playwright test. Playwright Test runs headlessly by default, so no browser window opens. For a script that launches a browser directly, use await chromium.launch({ headless: true }); headless is also the default for browser launches.

1. Install Playwright and its browsers

Install the Playwright package in your project, then download the browser binaries that match that package version:

npm install -D @playwright/test
npx playwright install

If your tests only use Chromium, install that browser instead:

npx playwright install chromium

On Linux CI machines, install the operating-system dependencies too:

npx playwright install --with-deps chromium

Playwright versions expect particular browser revisions. Run the install command again whenever you upgrade Playwright.

2. Run Playwright tests headlessly

The standard command is:

npx playwright test

Useful variations:

# Run one test file
npx playwright test tests/example.spec.ts

# Run a configured browser project
npx playwright test --project=chromium

# Show the browser window for debugging
npx playwright test --headed

# Open Playwright's interactive debugger
npx playwright test --debug

Headless execution is the normal mode for local automation, CI, containers and scheduled jobs. Add --headed only when you need to see the browser.

3. Make headless mode explicit in the test configuration

The default is already headless, but declaring it in playwright.config.ts makes the intent visible and gives you a single place to change it:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    headless: true,
  },
});

Set headless: false temporarily when diagnosing a test that behaves differently in a visible browser.

4. Launch a headless browser from a Node.js script

Use the browser API when you are writing a scraper, screenshot job or custom automation process instead of a Playwright Test suite:

import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
console.log(await page.title());

await browser.close();

Because headless is the launch default, this also works:

const browser = await chromium.launch();

Set the option explicitly when configuration may be supplied by another module or environment:

const browser = await chromium.launch({
  headless: process.env.HEADED !== '1',
});

5. A complete headless test example

Create tests/example.spec.ts:

import { test, expect } from '@playwright/test';

test('page has the expected title', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example Domain/);
});

Run it without opening a window:

npx playwright test tests/example.spec.ts

Run the same test visibly while investigating selectors or timing:

npx playwright test tests/example.spec.ts --headed

6. Python Playwright in headless mode

Install the Python package and matching browsers:

pip install playwright
playwright install chromium

The synchronous API launches headlessly when headless=True:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

For asynchronous applications:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        await page.goto("https://example.com")
        print(await page.title())
        await browser.close()

asyncio.run(main())

7. Choose the Chromium headless implementation

Playwright documents two Chromium headless paths. With no channel specified, the default uses a separate Chromium headless shell. You can opt into Chromium’s newer headless mode with channel: 'chromium'.

Default headless shell

This is the usual choice for headless CI. If you only need this path, install the smaller shell-only browser set:

npx playwright install --with-deps --only-shell

New Chromium headless mode

Use the chromium channel when closer alignment with regular Chrome matters or when you need browser-extension testing:

import { chromium } from 'playwright';

const browser = await chromium.launch({
  channel: 'chromium',
  headless: true,
});

Install without the separate shell when using this mode:

npx playwright install --with-deps --no-shell

The two implementations can differ in rendering and browser behavior. Select the one that matches your target environment, then verify important flows in that environment.

8. Run headless Playwright in CI

A minimal CI sequence is:

npm ci
npx playwright install --with-deps chromium
npx playwright test

Cache dependencies only when your CI system safely invalidates the cache after a Playwright version change. A stale browser revision commonly causes launch errors.

Headless mode does not require a display server. If you switch to headed execution on a Linux agent, provide Xvfb:

xvfb-run npx playwright test

Keep headed mode for diagnosis rather than normal CI runs.

9. Debug launch and test failures

Enable browser-process logging when Chromium will not start:

DEBUG=pw:browser npx playwright test

Enable Playwright API-operation logging for navigation, locator and timeout details:

DEBUG=pw:api npx playwright test

When a failure is timing-sensitive, rerun with:

npx playwright test --headed --debug

10. Configuration options that matter in headless runs

Option Use Example
headless Choose visible or invisible execution. headless: true
channel Select the newer Chromium headless implementation. channel: 'chromium'
--project Run one configured browser/device project. --project=chromium
--headed Open a visible browser for diagnosis. npx playwright test --headed
--debug Pause through an interactive debugging session. npx playwright test --debug
use.baseURL Keep test URLs concise and consistent. baseURL: 'http://localhost:3000'
use.trace Capture a trace for failed or selected runs. trace: 'on-first-retry'

Set shared options in playwright.config.ts so local and CI runs use the same browser context defaults.

11. Common errors and fixes

Error or symptom Cause Fix
Executable doesn't exist The browser revision was not downloaded, or Playwright was upgraded. Run npx playwright install; on Linux use --with-deps.
Browser starts locally but not in CI Missing Linux libraries, sandbox restrictions or an incompatible base image. Run npx playwright install --with-deps chromium and inspect DEBUG=pw:browser output.
Target page, context or browser has been closed The browser process crashed or application code closed it early. Check browser logs, memory limits and the order of page, context and browser.close().
Navigation timeout The page is slow, blocked or waiting for resources that never finish. Use an appropriate waitUntil, investigate network access and set a deliberate timeout instead of retrying blindly.
Headless rendering differs from headed rendering Different Chromium headless implementations, viewport, fonts or GPU behavior. Compare the default shell with channel: 'chromium', pin the viewport and install required fonts.
Headed mode fails with display errors Linux CI has no display server. Use headless mode or run headed tests under xvfb-run.
Tests pass alone but fail in parallel Shared state, ports, files or accounts collide between workers. Isolate fixtures and data, or reduce workers for the affected project.

12. Performance and reliability practices

  • Reuse a browser process when running many independent pages, while creating isolated contexts for separate users or tests.
  • Keep waits tied to observable application state, such as a selector or response, instead of long fixed delays.
  • Use the smallest browser set required by the suite; Chromium-only installation reduces setup work.
  • Pin the Playwright version and reinstall browsers during every clean CI build.
  • Set explicit viewport, locale, timezone and user-agent values when screenshots or layout assertions must be repeatable.
  • Capture traces on the first retry so intermittent failures have diagnostic evidence without producing a trace for every passing test.
  • Use retries sparingly. A retry can identify a flaky test, but it does not repair a deterministic selector or environment problem.

13. Cost and operational notes

Playwright itself is an open-source automation library, but headless jobs still consume CPU, memory, storage and CI minutes. Browser downloads also take time on fresh workers. Reusing browser processes, selecting only required browsers and caching safely can reduce setup overhead. The right headless implementation depends on whether you prioritize a small CI install or fidelity to regular Chrome.

Or skip the browser setup

If your goal is a clean website screenshot rather than browser automation, ScreenshotNeo provides a single HTTP request. Its API accepts the URL and returns PNG, JPEG, WebP or PDF; the full option set is documented at the ScreenshotNeo API docs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Before capture, ScreenshotNeo can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed; response headers identify the page verdict and whether the request was billed. Its MCP server gives Claude, Cursor and other MCP clients take_screenshot, get_page_info and capture_pdf tools. The Free plan includes 1,000 screenshots per month without a card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Does Playwright run headlessly by default?

Yes. Playwright Test runs headlessly by default, and direct browser launches default to headless.

What command opens the browser window?

Use npx playwright test --headed.

Do I need Xvfb for headless tests?

No. Xvfb is needed when you run headed tests on a Linux machine without a display.

Which Chromium headless mode should I choose?

Use the default shell for a small headless CI setup. Use channel: 'chromium' when behavior closer to regular Chrome or extension testing is important.

Why did headless mode stop working after an upgrade?

The new Playwright package may require different browser binaries. Run the matching npx playwright install command again and install Linux dependencies when required.