# How to Configure Headless Mode in Zendriver

> Learn to configure headless mode in Zendriver by setting headless=True in your Config object. This guide streamlines your automated browser tasks for enhanced efficiency.

- Repository: [CDP Driver/zendriver](https://github.com/cdpdriver/zendriver)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Zendriver enables headless mode by setting `headless=True` in the `Config` object, which appends `--headless=new` to Chrome launch arguments and automatically masks the "Headless" user-agent token via `Connection._prepare_headless()`.**

Zendriver is an open-source Python library that controls Chromium-based browsers through the Chrome DevTools Protocol (CDP). Configuring headless mode allows you to run browser automation without a visible UI, which is essential for server deployments and CI/CD pipelines. This guide explains how to configure headless mode in Zendriver using the central `Config` object and how the library handles user-agent masking automatically.

## Three Methods to Enable Headless Mode in Zendriver

Zendriver provides three distinct approaches to enable headless mode, depending on whether you want simplicity, full configuration control, or manual argument management.

### Using Browser.create() for Quick Configuration

The simplest way to enable headless mode is passing `headless=True` directly to `Browser.create()`. In [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py), the `create()` method forwards this flag to a new `Config` instance at lines 73-91.

```python
from zendriver import Browser

async with Browser.create(headless=True) as browser:
    page = await browser.main_tab.new_page("https://example.com")
    await page.wait_for_load()
    print(await page.title())

```

When you use this approach, `Config(headless=True)` is created internally, handling both the launch argument and user-agent preparation automatically.

### Using a Custom Config Object

For production scenarios requiring additional flags, instantiate `zendriver.core.config.Config` directly with `headless=True`. This method, defined at lines 34-38 of [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py), stores the flag as `self.headless` while allowing you to configure sandbox settings, window size, and other options.

```python
from zendriver.core.config import Config
from zendriver import Browser

cfg = Config(
    headless=True,
    sandbox=False,
    window_size=(1920, 1080)
)

async with Browser.create(config=cfg) as browser:
    tab = await browser.main_tab.new_page("https://httpbin.org/headers")
    await tab.wait_for_load()
    content = await tab.content()

```

### Manual Argument Injection (Advanced)

If you need to add `--headless=new` explicitly while keeping `headless=False` for other logic, use `Config.add_argument()`. Note that when using this method, the automatic user-agent correction in `Connection._prepare_headless()` will not execute, as it depends on the `headless` attribute being `True`.

```python
from zendriver.core.config import Config
from zendriver import Browser

cfg = Config()
cfg.add_argument("--headless=new")
cfg.add_argument("--disable-gpu")

async with Browser.create(config=cfg) as browser:
    page = await browser.main_tab.new_page("https://example.com")

```

## How Headless Mode Works Under the Hood

When `headless=True` is set, Zendriver executes two distinct operations to ensure proper headless browser behavior.

### Command-Line Argument Injection

During browser launch, `Config.__call__()` checks the `self.headless` attribute. If true, it appends `--headless=new` to the argument list at lines 11-13 in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py). This starts Chrome without a visible window while maintaining full CDP functionality.

### Automatic User-Agent Masking

After the WebSocket connection opens, `Connection._prepare_headless()` queries the real user-agent string, strips the "Headless" token, and invokes `Network.setUserAgentOverride` via the CDP. This logic resides at lines 31-50 in [`zendriver/core/connection.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/connection.py), ensuring target websites see a normal-looking browser UA rather than a headless identifier.

## Summary

- **Zendriver controls headless mode through the `headless` parameter in the `Config` object**, located in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py).
- **Setting `headless=True` appends `--headless=new`** to Chrome arguments via `Config.__call__()` before process launch.
- **The library automatically masks headless indicators** by rewriting the user-agent through `Connection._prepare_headless()` after connection establishment.
- **Three configuration methods exist**: direct `Browser.create(headless=True)`, custom `Config` instances, or manual argument addition.
- **Manual `--headless=new` injection bypasses automatic UA correction**, which may be desirable for specific testing scenarios.

## Frequently Asked Questions

### What is the difference between `--headless` and `--headless=new` in Zendriver?

Zendriver specifically uses `--headless=new` (the new headless implementation) rather than the deprecated `--headless` flag. According to the source code in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py), when `headless=True`, the library explicitly appends `--headless=new` to ensure compatibility with modern Chrome versions and proper CDP functionality.

### Does headless mode in Zendriver mask the user-agent automatically?

Yes. When you set `headless=True` in the `Config` object, `Connection._prepare_headless()` automatically queries the browser's user-agent, removes the "Headless" identifier, and overrides it via the Chrome DevTools Protocol. This occurs in [`zendriver/core/connection.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/connection.py) after the WebSocket connection is established.

### Can I run Zendriver headless with a custom user-agent?

Yes, but the headless UA correction takes precedence. If you set both `headless=True` and a custom `user_agent` in `Config`, the `_prepare_headless()` method will still override the user-agent to strip the "Headless" token. To use a completely custom UA without automatic correction, set `headless=False` and manually add `--headless=new` via `add_argument()`.

### Why would I add `--headless=new` manually instead of using the headless flag?

Manual addition via `Config.add_argument("--headless=new")` is useful when you need the browser to run headless for the UI, but you want to keep `headless=False` in the configuration object for other logic branches, or when you explicitly want to avoid the automatic user-agent masking performed by `Connection._prepare_headless()`. This approach gives you raw headless behavior without Zendriver's stealth modifications.