browser-gateway
Allows the gateway to route and manage browser sessions hosted in Docker containers, providing a scalable infrastructure for browser automation with health monitoring and failover.
Provides a compatible WebSocket gateway for Puppeteer, allowing AI agents to connect to and control browser instances via a centralized proxy with load balancing.
Overview
One endpoint. Multiple providers. Automatic failover when one is saturated or goes down.
Your app connects to ws://gateway:9500/v1/connect. The gateway picks the best available provider based on health, capacity, and your routing strategy. Providers can be cloud CDP services, Docker containers, or local Chrome instances.
Related MCP server: BrowserMCP
Dashboard
A web dashboard ships with every install. Open http://localhost:9500/web after starting the gateway.
Overview. Active sessions, queue depth, provider health, connection endpoint, and a copy-paste quickstart for Puppeteer, Playwright, Stagehand, browser-use, and raw CDP.
REST API. Run screenshot, content extraction, and structured scraping endpoints from a form-driven UI, with profile selection and parameter reference inline.
Playground. Drive any provider live from the browser. Pick a provider and profile, type into the canvas as if it were a local browser, and watch the remote session in real time.
Features
Routing & reliability
Automatic failover - the next provider takes over the instant one fails, no client changes
Five load-balancing strategies - priority chain, round-robin, least-connections, latency-optimized, weighted
Per-provider concurrency limits - the gateway enforces
maxConcurrenton every backendRequest queue - connections wait when every provider is saturated instead of failing immediately
Cooldown - failing providers are skipped and recover automatically after a TTL
Health checks - periodic connectivity probes mark providers unhealthy before clients hit them
Graceful shutdown - active sessions drain cleanly on SIGTERM and SIGINT
Session reconnect - dropped clients resume against the same provider with cookies and page state intact
Webhooks - fire on provider down, recover, and queue-overflow events
REST API
Screenshot -
POST /v1/screenshotreturns any URL as PNG or JPEG, full-page or scoped to a selectorContent extraction -
POST /v1/contentreturns markdown, plain text, HTML, or a cleaned articleScrape -
POST /v1/scrapeextracts structured data via CSS selectors or full-page formatsPooled sessions - browser connections are reused across requests, like a database pool
Automatic retry - failed requests retry against a fresh page
Profiles — persistent browser state
Survive across sessions - cookies,
localStorage,sessionStorage, andIndexedDBare captured on disconnect and replayed on the next connect with the same idOne-line opt-in - add
?profile=acmeto the WebSocket URL, the rest is automaticEncrypted at rest - AES-256-GCM with envelope encryption, anti-swap binding, and a scrypt-derived KEK
Provider-agnostic - state is captured at the CDP level, so it replays against any provider
Per-profile locking - concurrent connects to the same id return HTTP 409 to prevent corruption
Export and import - encrypted
.bgpblobs are portable between gateway installsOne-click enable - the dashboard wizard generates a strong key in your browser and writes it to config
See the Profiles docs for the full guide, security model, REST endpoints, and limitations.
Session replay — see what the agent saw
Frame-accurate visual record of every routed session, captured via CDP
Page.startScreencastZero injection - no script runs inside the customer page, capture is fully out-of-band
Provider-agnostic - works with any backend that supports page screencast
Dashboard player - scrub through the recorded frames, switch between captured browser targets
Retention controls - configurable horizon, per-session byte cap, daily cleanup
See the Replays docs for the storage layout, REST endpoints, and tuning knobs.
MCP server for AI agents
Eight browser tools - navigate, snapshot, screenshot, viewport, interact, evaluate, close, status
Zero config - auto-detects Chrome and launches it on first tool use
Concurrent sessions - every agent gets its own browser, no shared state
Raw CDP - no Playwright or Puppeteer dependency
Compatible - Claude Code, Cursor, and any MCP-compatible client
Management
Dashboard - manage providers, watch sessions, and edit config from the browser
Provider CRUD - add, edit, delete, and test providers from the dashboard or API
Config editor - edit
gateway.ymlin-browser with syntax highlighting and validationAuth - token-based, with a secure HttpOnly cookie for the dashboard
Protocol-agnostic - works with Playwright, Puppeteer, and any WebSocket protocol
Quick Start
As a WebSocket Proxy (for applications)
npm install -g browser-gatewayCreate gateway.yml:
version: 1
providers:
primary:
url: wss://provider.example.com?token=${PROVIDER_TOKEN}
limits:
maxConcurrent: 5
priority: 1
fallback:
url: ws://my-playwright-server:4000
limits:
maxConcurrent: 10
priority: 2browser-gateway serveConnect from your app:
// For CDP providers
const browser = await chromium.connectOverCDP('ws://localhost:9500/v1/connect');
// For Playwright run-server providers
const browser = await chromium.connect('ws://localhost:9500/v1/connect');Or use the REST API — no WebSocket management needed:
# Screenshot
curl -X POST http://localhost:9500/v1/screenshot \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}' --output screenshot.png
# Extract content as markdown
curl -X POST http://localhost:9500/v1/content \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "formats": ["markdown"]}'Dashboard at http://localhost:9500/web.
As an MCP Server (for AI agents)
Add to your Claude Code or Cursor config:
{
"mcpServers": {
"browser-gateway": {
"command": "npx",
"args": ["browser-gateway", "mcp"]
}
}
}No config files needed. The agent gets navigate, snapshot, screenshot, click, type, and evaluate tools through the gateway's routing layer.
See the MCP docs for all options.
Authentication
Set BG_TOKEN to require a token (or put it in a .env file):
BG_TOKEN=my-secret-token browser-gateway serveWebSocket clients pass the token as
?token=query paramAPI clients use
Authorization: Bearer <token>headerDashboard shows a login form, sets a secure HttpOnly cookie
Health endpoint (
/health) is always public
CLI
# Proxy server
browser-gateway serve # Start the gateway + dashboard
browser-gateway serve --port 8080 # Custom port
browser-gateway serve --config path.yml # Custom config
# MCP server for AI agents
browser-gateway mcp # Auto-detect Chrome, zero config
browser-gateway mcp --headless # Headless mode (for CI/Docker)
browser-gateway mcp --cdp-endpoint ws:// # Connect to existing browser
browser-gateway mcp --config gateway.yml # Multi-provider with failover
# Utilities
browser-gateway check # Test provider connectivity
browser-gateway version # Print version
browser-gateway help # Show helpAPI
Endpoint | Method | Description |
| WebSocket | Connect to a browser (the core feature) |
| POST | Take a screenshot of any URL (docs) |
| POST | Extract page content as markdown, text, or HTML (docs) |
| POST | Extract data via CSS selectors or full-page formats (docs) |
| GET | Gateway health + provider status + pool status |
| GET | Active sessions |
| GET/POST | List or add providers |
| PUT/DELETE | Update or remove a provider |
| POST | Test provider connectivity |
| GET/PUT | Read or save config |
| POST | Validate YAML without saving |
| POST | MCP Streamable HTTP endpoint |
| GET | CDP discovery (for browser-use, Playwright, Stagehand) |
| GET | Health check |
Docker
Recommended: Docker Compose. The bundled docker-compose.yml mounts a named volume for state and a read-only gateway.yml from the host.
# Drop your gateway.yml next to docker-compose.yml, then:
docker compose up -dPlain docker run:
docker run -d \
-p 9500:9500 \
-v bg_data:/data \
-v ./gateway.yml:/app/gateway.yml:ro \
-e PROVIDER_TOKEN=xxx \
ghcr.io/browser-gateway/server:latestPersistence
Everything the gateway writes to disk lives under a single directory, BG_DATA_DIR (defaults to /data inside the image). Mount that as a named volume or a bind mount and all state survives container restarts and image upgrades. Today it contains:
profiles/— encrypted profile blobs (when profiles are enabled)
Future versions may add more subdirectories under the same root (cooldown state, session snapshots, captures). Mounting BG_DATA_DIR as one volume keeps every subsystem persistent without follow-up config changes.
Upgrades
State lives in the volume, code lives in the image. Pull the new image, recreate the container — no data lost:
docker compose pull
docker compose up -dThe container reads the same BG_DATA_DIR and the same gateway.yml. Profile blobs are versioned and the gateway reads older formats transparently.
Image tags
Tag | Updated on |
| published manually after a release |
| always points at the newest version |
Images are multi-arch (linux/amd64, linux/arm64), signed with Sigstore build provenance, and ship an SBOM. Verify with the GitHub CLI:
gh attestation verify oci://ghcr.io/browser-gateway/server:0.3.0 \
--repo browser-gateway/browser-gatewayHow It Works
Sessions without profile / recording / observability (the default) take the byte-pipe fast lane:
Client connects to
ws://gateway:9500/v1/connectGateway selects a provider using your routing strategy
Gateway opens a raw TCP connection to the provider
HTTP upgrade forwarded, provider responds with
101 Switching ProtocolsBidirectional TCP pipe:
client <-> gateway <-> providerAll WebSocket messages forwarded transparently (never parsed or modified)
On disconnect: session cleaned up, slot released, metrics updated
If all providers full: connection waits in a queue until a slot opens
Sessions with profile inject, session recording, live view, or observability (e.g. ?profile=X, ?session_record=true, /v1/live) run through a CDP-aware pipeline instead — one WebSocket per session, N plugins observing the wire. Same routing + failover, byte-perfect passthrough at rest, plugins only fire when their feature is requested. Architecture + plugin-authoring guide: docs/PIPELINE.md.
Self-hosted provider: browserserve
browserserve is the stack's own self-hosted browser server: one container that hands out isolated Chrome sessions over CDP. Add it like any other provider:
providers:
browserserve:
url: ws://your-host:9222
cloud-provider:
url: <websocket-url-with-auth>
priority: 2Because the gateway controls that runtime, a browserserve provider is auto-detected and unlocks two things no external provider gets:
Auto capacity. You do not set
maxConcurrent. browserserve measures its host (memory, thread, and CPU limits) and advertises a safe ceiling, which the gateway adopts. The dashboard shows it as(auto).Multiple profiles from one slot. A browserserve provider can serve any profile, switching safely because every session is a fresh browser with no shared state. External providers stay single-profile-pinned, since reusing a browser leaks cookies and storage between profiles.
A common shape: browserserve as the primary provider on your own hardware, with a cloud provider at a lower priority for failover.
Works With
browser-gateway is compatible with existing browser tools. Just pass the gateway URL — it auto-resolves via /json/version.
AI Agent Frameworks:
# browser-use (Python) — HTTP URL auto-resolves
BrowserSession(cdp_url="http://localhost:9500")// Stagehand (TypeScript)
new Stagehand({ env: "LOCAL", localBrowserLaunchOptions: { cdpUrl: "http://localhost:9500" } })Playwright MCP (all 70 Playwright tools through gateway routing):
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--cdp-endpoint", "http://localhost:9500"]
}
}
}Puppeteer / Playwright:
// Playwright — HTTP or WebSocket
const browser = await chromium.connectOverCDP("http://localhost:9500");
// Puppeteer — WebSocket
const browser = await puppeteer.connect({ browserWSEndpoint: "ws://localhost:9500/v1/connect" });Documentation
Full docs live at docs.browsergateway.com.
Integrations — Playwright, Puppeteer, browser-use, Stagehand, Playwright MCP
Contributing
Contributions welcome. See CONTRIBUTING.md for guidelines.
License
MIT - see LICENSE.
Links
Contact
Questions, security reports, or partnership inquiries: hello@browsergateway.com.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted browser for AI agents: screenshots, post-JS DOM, console, WCAG. No install, no API key.
Undetectable cloud browser sessions for AI agents and scrapers. Navigate, extract, click, captcha.
- openhelmOAuthai.openhelm
Autonomous cloud agent tasks: real browser + your tools, structured evidence-backed results.
Stealth web automation for AI agents. Login, signup, navigate, screenshot.
Related MCP Servers
- AlicenseBqualityFmaintenanceA server that leverages Playwright for automated browser testing and integrates with Cloudflare Workers, enabling AI assistants to control web browsers for navigation, interaction, and screenshots.2225,247,752 npm258Apache 2.0
- AlicenseNot gradedqualityDmaintenanceRemote browser instances for your AI agents. Reliably complete any browser-based task at scale. Fully-control agentic browsers that spin up in seconds.14 npm40Apache 2.0
- AlicenseAqualityNot gradedmaintenanceEnables browser automation through Playwright using accessibility tree snapshots instead of screenshots. Supports web scraping, form interactions, testing, and connecting to existing browser sessions with logged-in accounts.8234,591 npm5-
- AlicenseNot gradedqualityAmaintenancesingle-binary MCP server that gives AI agents a browser. 66 tools for navigation, form filling, data extraction, screenshots, and DOM diffing — built on pure Chrome DevTools Protocol.13MIT