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.
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.


