March 15, 2026 · 3 min read
What is BrowserGateway?
The open-source router for cloud browsers. One connection URL. Every provider. MIT license.
If you're building anything with headless browsers at scale, you end up running more than one provider. Browserless, Steel, Browserbase, Cloudflare Browser Rendering, self-hosted Chrome — different jobs pull you toward different backends. But the moment you pick one, you inherit their outage schedule, their pricing changes, their region coverage, their captcha capability. Adding a second provider means rewriting your connection code, re-implementing failover, re-storing cookies. That layer belongs one level down from your app code.
BrowserGateway is a router that sits in front of all of them so you write one connection URL and swap providers without changing a line of app code.
It's MIT-licensed. You self-host it, or you use the hosted version at browsergateway.com. Same code either way. Puppeteer, Playwright, Stagehand, browser-use, any MCP client — they all connect the same way.
The one-liner
Think OpenRouter, but for cloud browsers instead of LLMs. Same shape. Different domain.
What you get
Install it in two commands:
npm install -g browser-gateway
browser-gateway serveOr run the Docker image:
docker run -p 9500:9500 ghcr.io/browser-gateway/server:latestPut your providers in gateway.yml:
backends:
browserless-1:
url: wss://production-sfo.browserless.io/?token=${BROWSERLESS_TOKEN}
limits:
maxConcurrent: 5
priority: 1
steel-1:
url: wss://connect.steel.dev?apiKey=${STEEL_KEY}
limits:
maxConcurrent: 2
priority: 2
local-chrome:
url: ws://localhost:9222
priority: 3Then your app connects to one URL:
import puppeteer from "puppeteer-core";
const browser = await puppeteer.connect({
browserWSEndpoint: "ws://localhost:9500/v1/connect",
});
const page = await browser.newPage();
await page.goto("https://en.wikipedia.org/");
await page.screenshot({ path: "wiki.png" });The gateway picks the best available provider based on your priority ordering, real-time capacity, latency, and cost. If Browserless is saturated, it falls over to Steel. If Steel is in cooldown from a health-check failure, it falls over to local Chrome. Your code doesn't notice.
What it does beyond routing
- Persistent browser profiles. Cookies, localStorage, IndexedDB captured at session end and injected on the next connect. Same profile survives across providers. Encrypted at rest with a key you control.
- Session replay. CDP screencast frames captured throughout a session, encoded to MP4 in the background, playable in the dashboard. Debugging failed agent runs stops being guesswork.
- MCP server built in, exposed at
POST /mcp. AI agents (Claude, Cursor, any MCP client) request browsers through the same routing layer. - REST API for one-shot screenshot, content, and scrape. Sits atop a pooled internal Chrome.
- Dashboard at
http://localhost:9500/web— providers, sessions, profiles, replays, config editor, live playground. - Five routing strategies — priority chain, round-robin, least-connections, weighted, latency-optimized. Configurable at runtime.
- Webhooks on session start / end / recorded / profile updated.
Why we built it
We were running a browser fleet across three providers and constantly writing glue code: switch here, retry there, capture the cookies before the session dies, replay them on the next box. Every incident was a new patch on the same shape of problem. Eventually the glue code was doing more work than the app code, and the honest answer was that this belonged one layer down — as an infrastructure primitive, not as a growing pile of imports.
We looked around for what existed. Browserless is a browser provider, not a router. Steel is a browser provider, not a router. Browserbase is a browser provider, not a router. Nothing sat where the routing layer needed to sit. So we built it.
Provider-friendly by design
BrowserGateway routes to the providers you already pay for. We don't operate browsers ourselves. We're not competing with the browser services — we complement them by making it painless to run more than one at once.
The repo is on GitHub under MIT. Try it. Tell us what breaks.