Skip to main content
The relay server bridges Playwright with the Playwriter Chrome extension over WebSocket.

Starting the Server

startPlayWriterCDPRelayServer

Starts a WebSocket relay server that connects Playwright to your Chrome browser via the extension.
import { startPlayWriterCDPRelayServer } from 'playwriter'

const server = await startPlayWriterCDPRelayServer({
  port: 19988,
  host: '127.0.0.1',
  token: 'optional-auth-token',
  logger: console,
})

// Later:
server.close()
Parameters:
port
number
default:"19988"
WebSocket server port
host
string
default:"127.0.0.1"
Host to bind the server to
token
string
Optional authentication token for remote access
logger
object
Logger with log(...) and error(...) methods (e.g., console)
Returns: Promise<RelayServer>
type RelayServer = {
  close(): void
  on<K extends keyof RelayServerEvents>(event: K, listener: RelayServerEvents[K]): void
  off<K extends keyof RelayServerEvents>(event: K, listener: RelayServerEvents[K]): void
}

Getting the CDP URL

getCdpUrl

Returns the WebSocket URL for connecting Playwright to the relay server.
import { getCdpUrl } from 'playwriter'

const url = getCdpUrl({ port: 19988 })
// 'ws://127.0.0.1:19988/cdp'
Parameters:
port
number
default:"19988"
The relay server port
Returns: string - WebSocket URL for Playwright

Connecting with Playwright

Once the server is running, connect Playwright using connectOverCDP:
import { chromium } from 'playwright-core'
import { startPlayWriterCDPRelayServer, getCdpUrl } from 'playwriter'

const server = await startPlayWriterCDPRelayServer()
const browser = await chromium.connectOverCDP(getCdpUrl())

const page = browser.contexts()[0].pages()[0]
await page.goto('https://example.com')

// Don't call browser.close() - it closes the user's Chrome!
server.close()
Never call browser.close() when connected to the user’s Chrome - it will close all their tabs!

Remote Access

For remote access over the internet or LAN, use the token parameter: Host:
npx -y playwriter serve --token my-secret
Client:
const browser = await chromium.connectOverCDP(
  'ws://remote-host:19988/cdp?token=my-secret'
)
See Remote Access for more details on tunneling with traforo.

Server Events

The relay server emits events you can listen to:
server.on('cdp:command', ({ clientId, command }) => {
  console.log('CDP command from Playwright:', command.method)
})

server.on('cdp:event', ({ event, sessionId }) => {
  console.log('CDP event from extension:', event.method)
})

server.on('cdp:response', ({ clientId, response }) => {
  console.log('CDP response to Playwright')
})

Multiple Extensions

If you have multiple Chrome profiles with Playwriter installed, specify which one to use:
const url = getCdpUrl({ port: 19988, extensionId: 'profile:user@example.com' })
const browser = await chromium.connectOverCDP(url)
The relay server automatically selects the extension with active tabs if only one is active.

Build docs developers (and LLMs) love