← Blog

BLOG

Solutions Remote Browser: Hosted Chromium for Agents

Solutions remote browser for AI agents and automation teams: hosted Chromium, CDP access, Playwright compatibility, and production criteria that matter.

September 14, 20269 min readRemote Browser

# Solutions Remote Browser: Hosted Chromium for Agents

When teams search for solutions remote browser, they usually have the same problem: browser automation works on a laptop and falls apart in production. The script depends on a local Chrome install, a developer's logged-in profile, and a machine that never sleeps. None of those hold up once an AI agent or a test harness needs to run continuously, in parallel, or from a server that has no display.

A remote browser solves this by moving the browser off your machine and onto infrastructure you connect to over the network. This guide covers what that actually means in practice, how Playwright and CDP fit in, and the criteria worth checking before you commit to a runtime.

What "solutions remote browser" actually refers to

A remote browser is a Chromium instance running on a server, exposed through a connection endpoint. Your code — a Playwright script, a Puppeteer client, a Selenium grid node, or an AI agent — attaches to that endpoint instead of launching a local binary.

The practical difference is where state and compute live:

  • Local browser: binary, profile, cookies, and rendering all sit on the machine running your code.
  • Remote browser: the browser runs in a hosted session; your code sends commands and receives events over CDP or a vendor SDK.

For a single developer debugging a script, local is fine. For an agent that needs to log into a dashboard at 3 a.m., hold a session across retries, and expose a live view for a human to intervene, the local model breaks down quickly. That gap is what most "solutions remote browser" searches are really about.

If you want the broader architectural framing first, Remote browsers for AI agents covers the runtime layer in more depth.

Why local Chrome stops working in production

The failure modes are predictable, and they show up in roughly this order:

  1. No display. Headless works until a site behaves differently without a GPU or a real viewport. Some flows need a headed browser, which a bare CI container cannot provide.
  2. Profile drift. Cookies, localStorage, and session tokens live in a local user-data directory. Move the job to another worker and the login is gone.
  3. Version skew. Playwright pins a Chromium build. Your CI image, your laptop, and your colleague's machine drift apart, and a selector that worked yesterday fails today.
  4. Resource contention. Ten parallel Chromium processes on one box exhaust memory. Scaling means provisioning more boxes, which is now your problem.
  5. No observability. When an agent fails at step 14, you have a stack trace and nothing else. There is no live view, no DOM snapshot, no video.

Each of these is solvable locally with enough engineering. The question is whether browser infrastructure is the thing you want your team maintaining. Most teams decide it is not.

How Playwright connects to a remote browser

Playwright has two connection paths, and they behave differently. Choosing the wrong one is the most common source of confusion.

connectOverCDP

connectOverCDP attaches to a browser that is already running and already has a CDP endpoint. You get the existing browser context, including its cookies and open tabs. This is the path you use when the browser is hosted for you.

import { chromium, Browser, BrowserContext, Page } from 'playwright';

async function attachToRemoteBrowser(cdpUrl: string): Promise<void> {
  // cdpUrl looks like: wss://<session-host>/cdp/<session-id>
  const browser: Browser = await chromium.connectOverCDP(cdpUrl, {
    timeout: 30_000,
  });

  // A hosted session usually exposes one default context.
  const contexts: BrowserContext[] = browser.contexts();
  const context: BrowserContext = contexts[0] ?? (await browser.newContext());

  const page: Page = context.pages()[0] ?? (await context.newPage());

  await page.goto('https://example.com/dashboard', {
    waitUntil: 'domcontentloaded',
  });

  // Reuse the session's existing auth state instead of logging in again.
  const title: string = await page.title();
  console.log(`Attached to session, page title: ${title}`);

  // Close the client connection, not the remote browser.
  await browser.close();
}

Two details matter here. First, browser.close() on a CDP connection disconnects your client; whether the remote session terminates depends on the runtime's session policy. Second, connectOverCDP is Chromium-only. If your stack needs Firefox or WebKit, this is not the path.

The official reference is the Playwright `browserType.connectOverCDP` documentation, which is worth reading before you wire up retry logic.

launch with a remote endpoint

The alternative is to let Playwright launch the browser, but point it at a remote debugging port or a vendor-provided WebSocket URL via launchOptions. This gives you more control over launch arguments — viewport, locale, proxy settings, and Chromium flags.

const browser = await chromium.launch({
  // Some runtimes accept a wsEndpoint here instead of a local binary.
  args: ['--disable-dev-shm-usage', '--no-sandbox'],
});

In practice, hosted runtimes usually hand you a CDP URL and expect connectOverCDP. That keeps session lifecycle on the server side, where it belongs.

Playwright MCP and browser extensions: what they do and do not solve

There is a lot of search traffic around "playwright mcp chrome extension" and "playwright chrome extension install." It is worth being precise, because these are different tools with different jobs.

  • Playwright MCP server: exposes browser control as MCP tools so an LLM client can drive a browser. It is a protocol adapter, not a browser runtime. It still needs a browser to attach to.
  • Chrome extensions: run inside a browser you already control. They are useful for instrumenting a user's own session, but they do not give you a headless, server-side, multi-tenant browser.
  • `connectOverCDP`: the actual transport. Whether the caller is an MCP server, a Python agent, or a TypeScript test, the connection to a remote browser goes through CDP.

The practical takeaway: an MCP layer or an extension can sit on top of a remote browser, but neither replaces one. If your agent needs to run without a human's laptop open, you need a hosted session underneath.

What to evaluate in a remote browser runtime

Not all hosted browsers are equivalent. These are the criteria that separate a demo from something you can run a business on.

CriterionWhy it mattersWhat to check
Connection protocolDetermines client compatibilityNative CDP endpoint; works with Playwright, Puppeteer, Selenium
Session isolationPrevents cross-tenant data leakageOne browser per session, no shared profile directories
Persistent profilesKeeps logins across runsProfile reuse across sessions, explicit lifecycle controls
Live viewerEnables human-in-the-loop debuggingReal-time view of the running session, not just a recording
Proxy and network controlsAffects geo-specific and rate-limited sitesConfigurable proxy settings, per-session network config
Browser settingsReduces bot-detection frictionConfigurable browser settings, not hardcoded defaults
Usage controlsKeeps spend predictablePer-session metering, caps, and clear billing units
ObservabilityMakes failures debuggableSession logs, DOM snapshots, video or screenshots

Two of these deserve emphasis. Session isolation is the one teams underweight until an incident forces the issue: if two agents share a profile directory, one agent's cookies can leak into another's run. Live viewer is the difference between "the agent failed" and "the agent failed because the page rendered a cookie banner that shifted the button." Debugging without a live view is guesswork.

For a closer look at how sessions behave over time, Remote browser online walks through the lifecycle from creation to teardown.

Where a hosted runtime fits in an agent stack

A useful mental model is to separate three layers:

  1. Reasoning layer. The LLM or agent framework deciding what to do next.
  2. Control layer. Playwright, Puppeteer, or an MCP server translating decisions into browser commands.
  3. Runtime layer. The browser itself — where it runs, how it persists, who can see it.

Most teams spend their effort on layers 1 and 2 and treat layer 3 as an afterthought. That is backwards. The runtime determines whether your agent can hold a session, survive a retry, and be debugged when it fails. Reasoning quality does not matter if the browser crashed at step 3.

Remote Browser is a runtime layer: hosted Chromium sessions with CDP access, Playwright and Puppeteer compatibility, persistent profiles, a live viewer, and per-session usage controls. It does not replace your agent framework or your control layer — it sits underneath both.

Production criteria before you migrate

Before moving a working local script to a hosted runtime, check these:

  • Does your client library support CDP attachment? Playwright, Puppeteer, and Selenium all do. Custom HTTP wrappers may not.
  • How does the runtime handle session timeouts? A session that dies mid-task needs a reconnect strategy, not a silent failure.
  • Can you pin browser settings per session? Locale, timezone, viewport, and proxy should be configurable at session creation, not globally.
  • What is the billing unit? Per browser-hour is common. Understand what counts as an hour and whether idle time is metered. Current details are on /pricing.
  • Is there a live view? If not, budget extra time for debugging.
  • How do you handle secrets? Credentials should be injected at session creation, not hardcoded in scripts.

The migration itself is usually small. Replace chromium.launch() with chromium.connectOverCDP(cdpUrl), move profile and proxy configuration to session creation, and add a reconnect path for dropped connections. The rest of your script stays the same.

Common mistakes when adopting a remote browser

A few patterns show up repeatedly in teams making this transition:

  • Treating the CDP URL as permanent. Session URLs expire. Store the session ID, not the URL.
  • Calling `browser.close()` and expecting the session to end. On a CDP connection, this disconnects the client. Session termination is a separate API call.
  • Sharing one session across parallel tasks. Sessions are isolated for a reason. One session, one task.
  • Ignoring the live viewer until something breaks. Watch a run before you ship it. You will find issues that logs never surface.
  • Assuming `connectOverCDP` works with Firefox. It does not. Chromium only.

None of these are hard to fix. They are just easy to miss if you treat the remote browser as a drop-in replacement for a local one.

Getting started

The shortest path is to create a session, grab the CDP URL, and attach with connectOverCDP. From there, the work is in session lifecycle: how you create, reuse, and tear down sessions, and how you observe them while they run.

Start with the documentation for the connection details and session API. If you are still deciding whether a hosted runtime is the right fit, Remote web browser and Remote control browser cover adjacent use cases — from test harnesses to human-in-the-loop control.

The core point stands: a remote browser is not a convenience wrapper around local Chrome. It is a different runtime with different guarantees. Treat it that way, and the migration is straightforward. Treat it as a drop-in replacement, and you will rediscover every failure mode you were trying to escape.