What is a headless browser?
A headless browser is a web browser that runs with no visible window and takes its commands from a program instead of a person. It still loads each page and runs its JavaScript. Test runners and coding agents use one to operate a web app and read what the page shows.
A headless browser is often a regular browser started in headless mode, e.g. Chrome with the --headless flag. Chrome's documentation describes this mode as a way to run the browser "in an unattended environment, without any visible UI." Playwright, an open-source browser test framework, runs its tests headless by default in Chromium, Firefox, and WebKit. Outside testing, headless browsers save pages as screenshots or PDFs and collect data from websites.
A headless browser differs from an HTTP client, e.g. curl, which downloads a page's HTML but does not run its scripts. Headless browser testing runs a web app's end-to-end tests in a headless browser, so the page's scripts run.
Other software can also be tested without a screen. A command-line application has no window, so a test runs it and reads its text output. A desktop app usually needs a real or virtual display, even on a server.
How does a headless browser work?
A test or an agent drives a headless browser in six steps:
- The test runner starts the browser in headless mode.
- The test runner connects to the browser over a control protocol, e.g. the Chrome DevTools Protocol.
- The test sends a command, e.g. "go to the checkout page."
- The browser loads the page and runs its JavaScript. It lays out the page and draws it into memory instead of onto a screen.
- The browser returns what the test asks for, e.g. the text of an element.
- The test compares the result with its expected value and passes or fails.
In Chrome, the mode that the --headless flag starts shares its code with the regular browser. An older headless mode was a separate implementation that shipped in the same program. It shared the rendering engine but not the code in Chrome's own browser layer. Since Chrome 132, the old mode ships only as a separate program, chrome-headless-shell.
Puppeteer, an open-source JavaScript library from Google, controls Chrome over the Chrome DevTools Protocol. Selenium sends its commands through the W3C WebDriver standard to a browser driver, e.g. ChromeDriver, which passes them to the browser. The other steps are the same.
What is an example of headless browser testing?
Here is an illustrative example. Acme Co. sells furniture online. A developer asks a coding agent to "Let customers edit their delivery address during checkout." The agent changes the checkout page and adds this browser test:
test("address change keeps the cart", async ({ page }) => {
await addItemsToCart(page, 2);
await page.goto("https://shop.example.com/checkout");
await page.getByLabel("Delivery address").fill("12 Elm Street");
await page.getByRole("button", { name: "Save address" }).click();
await expect(page.getByText("12 Elm Street")).toBeVisible();
await expect(page.getByTestId("cart-item")).toHaveCount(2);
});
Acme's continuous integration (CI) server runs npx playwright test. Playwright starts Chromium headless, so no window opens anywhere. The test fills in the address, clicks the button, and reads the page. The address check passes, and the cart check fails:
Error: expect(locator).toHaveCount(expected) failed
Locator: getByTestId('cart-item')
Expected: 2
Received: 0
Timeout: 5000ms
The test runner exits with a nonzero exit code, so the CI job fails. Nobody watched the run, so this message is the only record of what the page did. The address saved, but the cart emptied.
The developer reruns the test on a laptop with npx playwright test --headed and watches a browser window. The cart empties as soon as the Save address button is clicked. This example is simplified. A real project would also record a trace of each headless run, e.g. with Playwright's trace option, so nobody has to rerun the test to see each step.
What changes when a coding agent writes the code?
A coding agent can drive a headless browser to check its own change. It runs a test script or calls browser actions through a Model Context Protocol (MCP) server. Many of these tools give the agent the page's accessibility tree, a structured view of the page's elements with their names and roles, instead of a screenshot. A sandbox often has no display, so an agent inside one runs the browser headless. Some browser MCP servers, e.g. Playwright MCP, open a visible window unless a setting turns on headless mode.
The larger change is that nobody watches the page. An agent's headless run reports only what its checks read, and those checks can be shallow, e.g. a test that confirms the page loaded and never reads the cart. By default the tree lists names and roles, not positions, so a Save address button hidden under a banner still appears in it.
A practical adjustment is to have the agent save evidence from each headless run, e.g. a screenshot of the final page, and state which outcome each check covers. A reviewer can then check a passing run against the request, not only a failing one. Running the app this way is one form of runtime verification.
What can headless testing miss?
A headless run can pass while the app still has a problem. These are the common gaps:
- Anything the test does not check. A price that overlaps the product name passes unless a check covers it. A headless test is only as strong as its test oracle.
- A different browser build. By default, Playwright runs headless Chromium tests on a separate headless shell, and the
channel: "chromium"setting moves them to the regular build. A bug can appear in one build only. - Timing. A headless run can click a button before the page's JavaScript attaches its handler. A headed run is often slower, so a race condition can make a test fail in one mode only. A team can mistake that failure for a flaky test.
- Errors in the browser. A hydration error can appear only as an error in the browser while the page looks right. A test that listens for neither page errors nor console messages passes.
- The setup around the browser. A CI server can have fewer fonts than a customer's laptop, and some tools open a headless browser in a smaller window, so the layout changes.
A passing headless run is not the same as complete coverage.
How is a headless browser different from a headed browser?
A headed browser shows its window on a screen, so a person can watch a test run and step in. People doing exploratory testing usually work headed for the same reason. A headless browser draws the same page into memory and reports it only through code.
Because Chrome's --headless mode runs the same browser code as a headed window, differences between the modes often come from the setup around the browser. A headless run is often a little faster, and many headless browsers can run at once on a server with no display. The table lists the other differences:
| Attribute | Headless browser | Headed browser |
|---|---|---|
| Window | None | Shown on a screen |
| Who observes | Test code or an agent | A person |
| Where it runs | Any machine, including a server with no display | A machine with a real or virtual display |
| Typical use | Automated test runs | Debugging a failed test |
FAQs
What is headless Chrome?
Headless Chrome is the Chrome browser started in headless mode, with no visible window. Chrome's headless mode shares its code with the regular browser. The older headless implementation ships as a separate program called the headless shell, which some test tools use for headless runs.
Is headless testing faster?
Headless testing is often a little faster than headed testing. The page still loads and runs its JavaScript in full. The larger gain is that many headless browsers can run at once on a server with no display.
Why does a test pass headless but fail headed?
A test that passes headless but fails headed often runs with different timing or on a different browser build. A headed run is often slower, which can expose a race condition. These causes can also make a test pass headed and fail headless.
Can an AI agent use a headless browser?
An AI agent can use a headless browser by running a test script or by calling browser actions through an MCP server. The agent reads the page through its accessibility tree or a screenshot. Its run reports only what its checks read, so a saved screenshot of the final page gives a reviewer something to look at.