# How to Set a Custom User Agent String with Zendriver: 2 Methods Explained

> Learn two methods to set a custom user agent string with Zendriver for browser automation. Easily configure global or tab-specific user agents for your testing needs.

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

---

**Zendriver provides two ways to set a custom user agent string: pass the `user_agent` parameter to `Browser.create()` at launch for a persistent global setting, or call `Tab.set_user_agent()` on a running tab to override it via Chrome DevTools Protocol (CDP) for that specific target.**

Zendriver is a Python library for browser automation built on the Chrome DevTools Protocol. Controlling the HTTP User-Agent header is essential for web scraping, testing mobile responsiveness, or bypassing bot detection. This guide explains how to set a custom user agent string with Zendriver using both launch-time configuration and runtime overrides.

## Method 1: Set User Agent at Browser Launch (Recommended)

The most reliable way to set a custom user agent is to pass it when creating the browser instance. This ensures the custom string is applied to all tabs and network requests from the start.

In [`zendriver/core/browser.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/browser.py), the `Browser.create()` method (lines 74-92) accepts a `user_agent` parameter and passes it to a `Config` instance. The `Config` class in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py) stores this value (lines 48-71) and converts it into a `--user-agent=` CLI flag in the `__call__` method (lines 13-15). Chrome processes this flag at launch, embedding the custom string into every request and the `navigator.userAgent` JavaScript property.

```python
import asyncio
import zendriver as zd

async def main() -> None:
    # Pass a custom user-agent string to the start helper

    browser = await zd.start(user_agent="MyApp/1.0 (+https://example.com)")
    tab = browser.main_tab

    # Verify that the page sees the custom string

    ua = await tab.evaluate("navigator.userAgent")
    print("navigator.userAgent →", ua)   # → MyApp/1.0 (+https://example.com)

    await browser.stop()

if __name__ == "__main__":
    asyncio.run(main())

```

This example follows the pattern shown in [`examples/set_user_agent.py`](https://github.com/cdpdriver/zendriver/blob/main/examples/set_user_agent.py) within the repository.

## Method 2: Override User Agent on a Running Tab

If the browser is already running, you can override the user agent for a specific tab using the Chrome DevTools Protocol. This affects only the current tab and subsequent network requests from it.

In [`zendriver/core/tab.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/tab.py), the `Tab.set_user_agent()` method (lines 1667-1704) invokes `cdp.network.set_user_agent_override()` to update the browser's network stack for that target. It can also update `navigator.userAgent` via an evaluation fallback if needed.

```python
import asyncio
import zendriver as zd

async def main() -> None:
    browser = await zd.start()
    tab = browser.main_tab

    # Override only for this tab

    await tab.set_user_agent(
        "AnotherAgent/2.5",
        accept_language="fr-FR,fr;q=0.9",
        platform="Linux x86_64"
    )

    # Check the new values

    print(await tab.evaluate("navigator.userAgent"))   # → AnotherAgent/2.5

    print(await tab.evaluate("navigator.language"))    # → fr-FR,fr;q=0.9

    print(await tab.evaluate("navigator.platform"))    # → Linux x86_64

    await browser.stop()

if __name__ == "__main__":
    asyncio.run(main())

```

## How the User Agent Override Works Under the Hood

Understanding the implementation details helps you choose the right approach for your use case.

### Launch-Time Configuration

When you pass `user_agent` to `Browser.create()`, the `Config` class in [`zendriver/core/config.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/config.py) appends the value as a `--user-agent=` command-line argument. Chrome reads this flag during startup, setting the user agent globally for the entire browser process. This method is the most robust because it requires no additional network calls after launch.

### Runtime CDP Override

The `Tab.set_user_agent()` method uses the Chrome DevTools Protocol command `Network.setUserAgentOverride`. This command instructs Chrome to replace the user agent string for all subsequent HTTP requests from the specified target. According to the source in [`zendriver/core/tab.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/tab.py), this override is target-specific, meaning other tabs continue using the default or globally configured user agent.

### Headless Mode Handling

When running Chrome in headless mode, the default user agent typically includes the substring `"HeadlessChrome"`, which many bot detection systems flag immediately. In [`zendriver/core/connection.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/connection.py), the `Connection._prepare_headless()` method (lines 46-48) automatically strips the `"Headless"` suffix from the user agent before applying the override, helping you avoid detection when using headless browsers.

## Summary

- **Pass `user_agent` to `Browser.create()`** (or `zd.start()`) to set a persistent custom user agent for the entire browser session via the `--user-agent` CLI flag.
- **Call `Tab.set_user_agent()`** on a running tab to override the user agent dynamically for that specific target using CDP commands.
- **Headless mode automatically sanitizes** the user agent string to remove the "Headless" identifier, reducing detection risk.
- **Verify your configuration** by evaluating `navigator.userAgent` in the browser to confirm the override took effect.

## Frequently Asked Questions

### Can I change the user agent after the browser has started?

Yes. While the launch-time method locks the user agent for the entire process, you can override it on specific tabs after startup by calling `Tab.set_user_agent()`. This method uses the Chrome DevTools Protocol to update the network stack for that target without restarting the browser.

### Does setting a custom user agent affect all tabs or just one?

It depends on the method you choose. Passing `user_agent` to `Browser.create()` applies the string globally to every tab and request in the browser instance. Conversely, `Tab.set_user_agent()` affects only the specific tab instance you call it on, leaving other tabs with their original user agent.

### How do I check what user agent the browser is currently using?

Evaluate the `navigator.userAgent` property in the browser context. In Zendriver, use `await tab.evaluate("navigator.userAgent")` to retrieve the current string. This confirms whether your custom override was applied successfully and helps debug detection issues.

### Will the headless mode user agent contain "HeadlessChrome"?

Not if you use Zendriver's built-in handling. The `Connection._prepare_headless()` method in [`zendriver/core/connection.py`](https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/connection.py) automatically strips the "Headless" suffix from the user agent string when running in headless mode, helping you avoid common bot detection filters that scan for this substring.