Browser Use With a Proxy: Set Up the AI Browser Agent

Published:

14 minute read

Acar Diveroli
Written by: Acar Diveroli
Faint IP rows behind a plus-cornered frame with the Proxynet wordmark, a thin x and the Browser Use logo, tagged INTEGRATION

Your Browser Use agent places a test order on your staging shop without trouble from your laptop. Moved to a cloud server, the same task opens the site from a datacenter address in another country: the shop shows a different currency, and your staging firewall, which only lets in known IPs, turns the agent away. You add a proxy as http://user:pass@pr.proxynet.io:8000, and now every page fails with net::ERR_NO_SUPPORTED_PROXIES.

This guide covers how Browser Use works, installation, a proxy with a username and password, the exit-IP check, sticky and rotating sessions, domain limits, secrets and a human approval step. We tested every script with browser-use 0.13.10 on Python 3.13 through a local test proxy that requires a password. With no LLM API key on the test machine, a scripted stand-in gave the model's replies; the browser, proxy, domain filter and secret masking ran for real.

What is Browser Use?

Browser Use is a Python library that turns a language model into a browser agent. You describe the task in plain words, and the model decides the clicks, typing and scrolling. The GitHub repository describes it as "Agents that use the browser"; it is MIT-licensed, and the latest release on PyPI today is 0.13.10, published on 4 September 2026, for Python 3.11 or newer.

Version 0.13.10 talks to Chromium directly through the Chrome DevTools Protocol (CDP); Playwright is not among its dependencies. The model can come from OpenAI, Anthropic, Google, a local Ollama server or Browser Use's own hosted models. Browser Use Cloud, a separate paid service with hosted browsers, is not part of this guide.

How an agent plans and uses tools in general is covered in How Do AI Agents Work?; browsers with an agent built in are explained in What Is an AI Browser?.

How does a Browser Use agent work?

One agent.run() goes through these steps:

  1. The browser starts with a fresh temporary profile. The proxy is passed to Chromium as a launch flag.
  2. A URL in the task is opened directly, without a model call.
  3. The page is turned into text. Every element the agent can use gets a number, and the visible text comes with it. With use_vision=True (the default) a screenshot is added.
  4. The model answers in JSON: an evaluation of the last step, a short memory, the next goal and up to five actions, such as navigate, click, input, scroll, extract or done.
  5. The library carries out the actions, checks each new URL against your domain rules and fills in secrets where the model wrote a placeholder.
  6. The loop repeats until the model calls done or max_steps runs out.

On a practice book shop, the browser state our stand-in model received contained lines like these:

text
[587]<a title=It's Only the Himalayas />
	It's Only the Himalayas
£45.17
[597]<i />
In stock
[604]<button type=submit />
	Add to basket

To add this book to the basket, the model would answer {"click": {"index": 604}}. Because the page arrives as text, use_vision=False still works and keeps screenshots of sensitive pages away from the model provider.

How do you install Browser Use?

The official quickstart uses uv:

bash
pip install uv
uv venv --python 3.12
source .venv/bin/activate        # Windows: .venv\Scripts\activate
uv pip install browser-use
uvx browser-use install          # downloads Chromium

We installed with plain pip install browser-use==0.13.10 into a Python 3.13 virtual environment, which worked as well. Then put the keys in a .env file next to your script:

bash
OPENAI_API_KEY=...
PROXY_SERVER=http://pr.proxynet.io:8000
PROXY_USERNAME=user
PROXY_PASSWORD=pass
ANONYMIZED_TELEMETRY=false

The last line matters. Browser Use sends usage telemetry by default, and its telemetry page says this may include task instructions, visited URLs, action traces and final results. If your tasks mention customers or internal sites, turn it off.

Two things surprised us on the Windows test machine. Without executable_path, Browser Use picked the installed Google Chrome, with a new temporary profile rather than your everyday one. On the first start it also downloaded three extensions, an ad blocker among them, straight from the Chrome Web Store and outside the proxy; enable_default_extensions=False turns them off.

How do you run an agent through a proxy?

The proxy is a setting of the Browser, and the agent receives that browser. This script opens one category page and reads three books, going out only through the proxy and only to one domain:

python
"""Run a Browser Use agent through an authenticated proxy, limited to one site."""
import asyncio
import os

from dotenv import load_dotenv

load_dotenv()  # reads OPENAI_API_KEY, PROXY_* and ANONYMIZED_TELEMETRY from .env

from browser_use import Agent, Browser, ChatOpenAI
from browser_use.browser import ProxySettings

browser = Browser(
    headless=True,
    proxy=ProxySettings(
        server=os.environ["PROXY_SERVER"],  # http://pr.proxynet.io:8000
        username=os.environ["PROXY_USERNAME"],
        password=os.environ["PROXY_PASSWORD"],
    ),
    allowed_domains=["books.toscrape.com"],
    enable_default_extensions=False,
)

agent = Agent(
    task=(
        "Open https://books.toscrape.com/catalogue/category/books/travel_2/index.html "
        "and list the title and price of the first three books."
    ),
    llm=ChatOpenAI(model="gpt-4.1-mini"),
    browser=browser,
    use_vision=False,
)


async def main():
    history = await agent.run(max_steps=10)
    print("result:", history.final_result())
    print("pages: ", [u for u in history.urls() if u])
    print("steps: ", history.number_of_steps())


asyncio.run(main())

ProxySettings is imported from browser_use.browser, not from the top-level package. The browser parameters page lists four fields:

FieldWhat it takes
serverhttp://host:port or socks5://host:port, without credentials
usernameProxy username, for example the one from the Endpoint Generator
passwordProxy password
bypassHosts that skip the proxy, comma-separated

Under the hood, Browser Use starts Chromium with --proxy-server and answers the proxy's 407 challenge itself through CDP. Our test proxy logged exactly that for the book page (shortened, timestamps removed):

text
407 CONNECT books.toscrape.com:443 (no credentials)
200 CONNECT books.toscrape.com:443 user=user

The proxy username and password did not appear in anything the model received.

Why does user:pass in the proxy URL fail?

Because Chromium ignores credentials written into its proxy setting. The Chromium proxy documentation says Chrome "will not use any credentials embedded in the proxy settings". With server="http://user:pass@…" and no separate fields, every navigation in our test ended with net::ERR_NO_SUPPORTED_PROXIES. Put the credentials in username and password.

SOCKS5 has a different limit: Chromium supports no authentication method for SOCKS5 at all. Our SOCKS5 test server saw the browser offer only "no authentication", and the page failed with ERR_SOCKS_CONNECTION_FAILED. For SOCKS5, add your server's IP to the IP whitelist in the dashboard (up to 10 addresses) and use the SOCKS5 port the Endpoint Generator shows, without a username. DNS then resolves on the proxy side. The two sign-in methods are compared in Proxy Authentication: User:Pass vs IP Whitelist, the protocols in SOCKS vs. HTTP Proxy.

How do you check the agent's exit IP?

Check before the first real task, without a model in the loop. This script uses the same ProxySettings, opens an IP echo page and prints what the site saw:

python
"""Open an IP echo page through the proxy before any agent runs."""
import asyncio
import os

from browser_use import Browser
from browser_use.browser import ProxySettings


async def main():
    browser = Browser(
        headless=True,
        proxy=ProxySettings(
            server=os.environ["PROXY_SERVER"],
            username=os.environ["PROXY_USERNAME"],
            password=os.environ["PROXY_PASSWORD"],
        ),
    )
    await browser.start()
    try:
        page = await browser.new_page("https://httpbin.org/ip")
        await asyncio.sleep(2)  # let the page load
        print(await page.evaluate("() => document.body.innerText"))
    finally:
        await browser.kill()


asyncio.run(main())

It prints httpbin's JSON with an origin field. Through a Proxynet endpoint, that is the exit IP, not your own. Run it twice: a rotating username can show two different addresses, a sticky one the same address both times.

Our proxy log also showed Chrome's own requests to Google services and the Browser Use logo on the blank start page; if you pay for proxy traffic by the GB, these count too.

Rotating or sticky: which session does an agent need?

The proxy belongs to the browser, so every step of a task goes out through the same proxy address. What changes is the exit IP behind that address.

TaskSessionWhy
Sign in, then act on several pagesSticky, 10-30 minutesMany sites tie a session to an IP; a new address mid-task can mean a new login
Read one independent page per runRotatingEach run may get a new exit; nothing carries over
Several agents in parallelOne Browser per agentEach browser has its own proxy settings and session
An address your own systems allow-listStatic ISPThe IP stays the same across days

On Proxynet you choose rotating or sticky, country and city in the Endpoint Generator, with a sticky length of 1 to 60 minutes; the username it produces already carries these choices, so paste it into PROXY_USERNAME as it is. Pick a sticky length a little longer than your longest task. Products: Sticky Proxy, Rotating Proxy, ISP Proxy, and Residential Proxy when a check needs a home connection in a given country. How rotation works is in What Is IP Rotation and How Does It Work?.

How do you limit where the agent can go?

allowed_domains takes a list of patterns: example.com, *.example.com for subdomains, http*://example.com for both schemes. Wildcards in the top-level domain are rejected. prohibited_domains works the other way; when both are set, the allow-list wins.

We allowed only our local shop and had the stand-in model try two exits. A direct navigate to another host returned this to the model, with about:blank as the current page:

text
Navigation failed: Navigation to http://partner.test:28140/ blocked by security policy

A link on the shop that redirected to the same host also ended on about:blank, and the model never saw that page's text. But the test site's own log showed that the browser had requested both off-list pages before the check replaced them, and an image from the off-list host on an allowed page loaded normally. In 0.13.10, the filter controls what the agent reads and acts on, not what the browser sends.

For a real boundary, limit outbound traffic outside the agent: a firewall or proxy-side allow-list, a separate container or user account, and no access to internal addresses. Playwright MCP has the same caveat for its origin flags, as covered in What Is Playwright MCP?. The wider pattern is in Safe Web Access for LLMs.

How do you keep credentials away from the model?

Never write a password into the task. The task goes to the model provider with every step and, unless you switch it off, into telemetry. Browser Use has a placeholder mechanism for this, sensitive_data: you pass real values in code, the model only sees names such as x_user and x_pass, and the library types the real value into the field after the model has answered.

In our test, the model's action was <secret>x_pass</secret>, the log printed "Typed <sensitive>", and when the page echoed the username back, the model received Signed in as <secret>x_user</secret>. The real values appeared in no model input. Three habits complete it:

  • Scope secrets to a domain, so a placeholder is only filled on the site it belongs to.
  • Set use_vision=False on pages where a screenshot could show the value.
  • Do not hand the agent your everyday Chrome profile (Browser.from_system_chrome()) for tasks that read outside pages: every signed-in session in it becomes reachable.

How do you add a human approval step?

Indirect prompt injection means text on a page that the model takes as an instruction. OWASP's LLM01 entry lists human approval for high-risk actions among the defenses. Browser Use calls register_new_step_callback after the model has chosen its actions and before they run, so the callback can stop the agent:

python
"""A Browser Use agent with an allow-list, placeholders for secrets and a human approval gate."""
import asyncio
import os

from dotenv import load_dotenv

load_dotenv()

from browser_use import Agent, Browser, ChatOpenAI
from browser_use.browser import ProxySettings

SITE = "https://shop.example.com"
READ_ONLY = {"navigate", "scroll", "find_text", "extract", "screenshot", "go_back", "wait", "done"}

browser = Browser(
    headless=True,
    proxy=ProxySettings(
        server=os.environ["PROXY_SERVER"],
        username=os.environ["PROXY_USERNAME"],
        password=os.environ["PROXY_PASSWORD"],
    ),
    allowed_domains=["shop.example.com"],
    enable_default_extensions=False,
)


def approve(browser_state, model_output, step):
    """Runs after the model has chosen its actions and before they are executed."""
    planned = [action.model_dump(exclude_none=True) for action in model_output.action]
    if all(next(iter(a)) in READ_ONLY for a in planned):
        return
    print(f"Step {step} wants to run: {planned}")
    if input("Allow? [y/N] ").strip().lower() != "y":
        agent.stop()


agent = Agent(
    task=f"Sign in at {SITE}/login with username x_user and password x_pass, then open the partner offer.",
    llm=ChatOpenAI(model="gpt-4.1-mini"),
    browser=browser,
    sensitive_data={"http*://shop.example.com": {"x_user": os.environ["SHOP_USER"], "x_pass": os.environ["SHOP_PASS"]}},
    use_vision=False,
    register_new_step_callback=approve,
)


async def main():
    history = await agent.run(max_steps=8)
    print("done:  ", history.is_done())
    print("errors:", [e for e in history.errors() if e])


asyncio.run(main())

We ran it against our local shop, with the domain changed. The sign-in step asked first; after "y" the agent typed both values and clicked. A later click asked again, and after "n" the log said "Agent stopping", the click never ran and is_done() returned False. Any action outside the read-only set, including typing, now waits for a person.

Browser Use or Playwright MCP?

Both let a model use a browser; they differ in who runs the loop.

Browser UsePlaywright MCP
Who runs the agent loopThe library, in your Python codeThe assistant you already use (Claude Code, Cursor)
Proxy with a passwordProxySettings fieldslaunchOptions.proxy in a config file
Domain limitallowed_domains--allowed-origins
SuitsAgents inside your own serviceAd hoc work from a coding assistant

Setup and flags for the second are in What Is Playwright MCP?.

Use cases

  • Localization checks on your own site: what language, currency and cookie notice a visitor from one country gets (localization).
  • Exploratory testing of your own app: walking a sign-up or checkout flow on staging from different locations (app testing).
  • One-off reads from a JavaScript page: a few values from a public page that renders in the browser (static vs dynamic pages). For regular jobs, a plain script is cheaper (Playwright with a proxy).
  • Clean text for a model instead of a browser: when the agent only needs to read, see What Is Crawl4AI?.

An agent that browses for you is still automated traffic. Browser Use 0.13.10 does not read robots.txt (we found no code for it in the package), so read the site's robots.txt and terms yourself, use an official API where one exists, keep to a few pages at a human pace and stop when a site says no. Why sites tell automated visitors apart is covered in Why Are AI Shopping Agents Blocked on Websites?. The stealth and CAPTCHA features advertised for Browser Use Cloud are outside this guide, and we do not recommend them.

Common mistakes

What you seeWhyWhat to do
net::ERR_NO_SUPPORTED_PROXIESCredentials written into serverMove them to username and password
net::ERR_TOO_MANY_RETRIESWrong proxy username or password; the proxy keeps answering 407Copy the credentials again from the dashboard
net::ERR_PROXY_CONNECTION_FAILEDWrong host or port, or outbound traffic blockedTest the same address with cURL
ERR_SOCKS_CONNECTION_FAILEDsocks5:// with a passwordUse the HTTP endpoint, or SOCKS5 with an IP whitelist
blocked by security policyThe URL is outside allowed_domainsAdd the domain, or leave it blocked
The agent signs in again halfwayA rotating exit changed the IP mid-taskUse a sticky session

Two habits cause trouble without an error: treating allowed_domains as a firewall, and running an unpinned version. The API has changed between releases, so pin the version you tested (browser-use==0.13.10) and read the release notes before upgrading.

Decision guide

NeedRecommendation
A first agent run through a proxyProxySettings with the HTTP endpoint, username and password; then the IP echo script
A multi-step task with a sign-inSticky session longer than the task
Independent single-page runsRotating session
Keeping the agent on one siteallowed_domains, plus an outbound allow-list outside the agent
Actions that change somethingAn approval callback that can call agent.stop()

Frequently asked questions

Is Browser Use free?

The library is free under the MIT licence. You pay for the model calls, at least one per step, and for proxy traffic. Browser Use Cloud and its hosted models are billed separately.

Does Browser Use still use Playwright?

Not for driving the browser. Version 0.13.10 controls Chromium through the Chrome DevTools Protocol; the install command uses Playwright's downloader for Chromium, and saved login state uses Playwright's file format.

Can I use a local model instead of an API?

The supported-models page shows ChatOllama(model="llama3.1:8b") for a local Ollama server. We did not test a local model.

Can two agents use different countries at the same time?

Yes, with two Browser objects, each with its own ProxySettings. One browser has one proxy for all of its tabs.

Does Browser Use respect robots.txt?

Not by itself; version 0.13.10 has no robots.txt check. Read the file before you give the agent a site.

Does a proxy keep websites from blocking my agent?

No. A proxy changes the address a request comes from; the browser's behaviour and the pace stay the same. A slow pace, permitted pages and the official API are the lasting route.

Summary

Browser Use gives a language model a real Chromium browser and runs the agent loop in your own Python code. Set the proxy on the browser with ProxySettings and separate username and password fields; credentials in the URL and SOCKS5 with a password both fail in Chromium. Check the exit IP before the first task, use a sticky session for anything with a sign-in, and treat allowed_domains as a filter for the agent, not as a network boundary. Keep secrets in sensitive_data, turn telemetry off and put a human in front of actions that change something. Suitable exits for your agent are on our proxy services page.

Ask ChatGPTAsk Claude