BrowserGateway

Session Lifecycle

Understanding what happens when connections open and close through the gateway.

Understanding what happens when connections open and close through the gateway.

What the Gateway Manages

The gateway manages the transport layer - the WebSocket pipe between your client and the provider. When a client connects, the gateway:

  1. Selects a provider
  2. Opens a TCP connection to the provider
  3. Forwards the WebSocket upgrade handshake
  4. Pipes all traffic bidirectionally
  5. Tracks the session (connected time, message count, provider assignment)

When either side disconnects, the gateway:

  1. Closes the other side of the pipe
  2. Releases the concurrency slot for that provider
  3. Updates metrics (duration, latency)
  4. Removes the session from tracking

What the Gateway Does NOT Manage

The gateway does not manage provider-specific session lifecycle. Some providers have their own concept of "sessions" that exist independently of the WebSocket connection.

For example:

  • Some cloud providers keep a browser session alive after you disconnect, expecting you to explicitly release it
  • Some providers auto-timeout idle sessions after a period (e.g., 15 minutes)
  • Self-hosted Playwright servers close the browser when the WebSocket closes (no cleanup needed)
  • Raw Chrome instances stay alive after the WebSocket disconnects

The gateway has no knowledge of these provider-specific behaviors. It's a transparent transport layer.

Best Practices

Self-hosted providers (Playwright, Chrome)

No special cleanup needed. The browser process is managed by the provider.

const browser = await chromium.connect('ws://gateway:9500/v1/connect');
await page.goto('https://example.com');
await browser.close();  // WebSocket closes, gateway cleans up. Done.

Cloud providers with managed sessions

If your cloud provider has session management (create/release lifecycle), handle it in your application code:

// Your application manages the provider session
const session = await provider.sessions.create();

// Connect through the gateway
const browser = await chromium.connectOverCDP('ws://gateway:9500/v1/connect');
await page.goto('https://example.com');
await browser.close();

// Your application releases the provider session
await provider.sessions.release(session.id);

The gateway handles the WebSocket transport. Your application handles the provider session lifecycle.

Why It Works This Way

The gateway is protocol-agnostic. It forwards raw bytes without understanding what they mean. This is what makes it work with any provider and any protocol. The trade-off is that provider-specific lifecycle management stays in the application layer, where it belongs.

This is the same model used by reverse proxies like nginx and HAProxy - they manage TCP/HTTP connections, not application sessions.

Connect URL Parameters

Every parameter is optional. Combine as needed.

ParameterPurpose
token=<BG_TOKEN>Auth when BG_TOKEN is set. Alternative to Authorization: Bearer.
provider=<id>Pin the request to one provider slot. Fails if that slot is unhealthy or full.
profile=<id>Load a saved profile into the session. Default is write-back. See Profiles.
readOnly=1Combined with profile=, load the profile but never save changes back. Bypasses provider eligibility because nothing is written.
session_record=trueRecord the session's frames to disk. No config switch needed. See Session Replays.
sessionId=<id>Reconnect to a previous session: the same browser when the provider can hand it back, otherwise the same provider. See Session Reconnection.

Connection States in the Gateway

You can check active sessions at any time:

curl http://localhost:9500/v1/sessions
{
  "count": 2,
  "sessions": [
    {
      "id": "abc123",
      "providerId": "primary",
      "profileId": null,
      "connectedAt": "2026-03-25T17:00:00.000Z",
      "lastActivity": "2026-03-25T17:05:30.000Z",
      "durationMs": 330000,
      "messageCount": 847
    }
  ]
}

Idle Timeout

The gateway has a configurable idle timeout. If no WebSocket messages pass through a session for the configured period, the gateway closes both sides:

gateway:
  sessions:
    idleTimeoutMs: 300000   # 5 minutes (default)

This protects against leaked connections where a client disconnects without a proper close handshake (e.g., network drop, crashed process). The provider's concurrency slot is freed when the idle timeout fires, and a session saving a profile saves it before closing. Versions before 0.4.27 logged idle sessions without closing them.

Session Reconnection

What the gateway does: when a client connects, the gateway assigns a session ID and remembers which provider that session was routed to. If the client disconnects and reconnects with the same session ID within the reconnect timeout, the gateway sends it back to the same provider. Whether the reconnect gets the same browser depends on the provider:

  • Providers that issue a resume token (browserserve 0.1.18 or later with its resume window switched on) keep the browser for their window when a connection breaks and hand the gateway a token on connect. On reconnect the gateway presents that token, so all browser state is intact: open tabs, page content, cookies, localStorage and scroll position. The token never reaches your client.
  • Other providers usually start a fresh browser for each connection, so the reconnect lands on the same provider but in a new browser.

The gateway does not keep the browser alive itself. It does not buffer messages, replay CDP traffic, or hold any browser state on its side.

gateway:
  sessions:
    reconnectTimeoutMs: 300000   # 5 minutes (default)

To reconnect, pass the session ID as a query parameter:

ws://gateway:9500/v1/connect?sessionId=<session-id>

The session ID is returned in the X-Session-Id response header when a connection is first established. The same response carries X-Session-Resumable: true when the provider issued a resume token, and a reconnect that got the same browser back answers X-Session-Resumed: true.

A disconnected session stays reconnectable (parked) for reconnectTimeoutMs. List parked sessions with GET /v1/sessions/parked. A reconnect that arrives after the window has passed, or after the provider has destroyed the browser, does not reattach: the gateway treats it as a new connection and routes it normally, with a fresh browser and a new session ID.

browserserve keeps a browser only when the connection breaks. A client that closes the connection normally, or calls browser.close(), ends the session.

This is useful for:

  • AI agents that crash mid-workflow and need to resume
  • Long-running automation jobs interrupted by network issues
  • Multi-step processes where losing browser state means starting over

Limitations:

  • Playwright's browser.close() closes the browser, so nothing is left to reconnect to
  • Does not survive gateway restart (session registry is in-memory)
  • Does not survive browser crash at the provider

On this page