# How Cypress Handles WebSocket Connections and Real-Time Communication

> Learn how Cypress handles WebSocket connections and real-time communication using its dedicated real-time layer. Discover how bidirectional messaging works between the test runner, browser, and driver.

- Repository: [Cypress.io/cypress](https://github.com/cypress-io/cypress)
- Tags: deep-dive
- Published: 2026-07-12

---

**Cypress implements a dedicated real-time communication layer through the `@packages/socket` package, which wraps socket.io with custom parsers and environment-specific adapters—including `CDPBrowserSocket` for Chromium-based browsers and `SocketBroadcaster` for the Node.js server—to enable bidirectional messaging between the test runner, browser, and driver.**

Cypress relies on low-latency WebSocket connections to power its interactive test runner, live command logging, and browser automation capabilities. The `cypress-io/cypress` repository centralizes this functionality in the `@packages/socket` package, which abstracts socket.io with Cypress-specific extensions for both client and server environments. Understanding this architecture reveals how the framework maintains persistent connections across page reloads while broadcasting real-time events to multiple concurrent test sessions.

## Architecture of Cypress WebSocket Connections

The `@packages/socket` package provides dual-environment implementations that share a common wire protocol but handle transport differently depending on whether code runs in the browser or Node.js.

### Browser-Side Client Implementation

The client-side logic resides in [`packages/socket/lib/client/browser.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/client/browser.ts), which exports the `createWebsocket` function and `client` helper. When a test launches, the browser client evaluates the `browserFamily` parameter to determine the transport strategy.

For **Chromium-based browsers** (Chrome, Edge), the code instantiates a `CDPBrowserSocket` defined in [`packages/socket/lib/client/cdp-browser.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/client/cdp-browser.ts). This specialized wrapper implements the Chrome DevTools Protocol over WebSocket and stores the socket instance on `window.cypressSockets` to survive page reloads. This persistence is critical when testing applications served from URLs containing basic authentication credentials, as Cypress must maintain the connection state across navigation events.

For **other browsers**, the client falls back to standard socket.io with the `cypressParser` custom serializer. The transport selection logic forces `transports: ['websocket']` for most browsers, but explicitly switches to `transports: ['polling']` for WebKit where the socket.io transport layer is known to be unstable.

### Server-Side Node Implementation

On the server side, [`packages/socket/lib/node/socket.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/node/socket.ts) initializes the socket.io server using the same `cypressParser` to ensure binary compatibility with clients. The [`packages/socket/lib/node/cdp-socket.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/node/cdp-socket.ts) module provides the server counterpart for CDP-specific communication, enabling the test runner to drive browser automation through the same channel used for test commands.

The `SocketBroadcaster` class, located in [`packages/socket/lib/node/socket-broadcaster.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/node/socket-broadcaster.ts), exposes methods to emit events to every connected client simultaneously. This broadcaster relays real-time updates such as `test:start`, `command:end`, and `network:request` to the Test Runner UI, browser driver, and any active CDP sockets.

## Connection Establishment and Transport Selection

When Cypress launches a browser, the driver calls `createWebsocket({path, browserFamily})` from [`packages/socket/lib/client/index.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/client/index.ts). The function returns different implementations based on the browser family:

- **Chromium**: Returns a `CDPBrowserSocket` instance that opens a WebSocket connection to `ws://<dev-server>/<path>/default`
- **Firefox/WebKit**: Returns a standard socket.io client configured with the custom parser

Each test run receives its own **namespace** constructed as `${opts?.path}${uri}`, ensuring that concurrent test sessions remain isolated. In Chromium environments, the namespace registration persists on `window.cypressSockets`, allowing the same socket to be reused even when the application under test performs full page reloads.

## Custom Protocol and Message Parsing

Both client and server utilize **`cypressParser`**, a custom patch to the socket.io parser located at `packages/socket/patches/socket.io-parser+4.2.6.patch`. This parser serializes Cypress-specific payloads—including command IDs, test state transitions, and network stubbing metadata—while maintaining efficient binary transmission.

The custom parser ensures that complex objects like DOM snapshots and network request details serialize correctly across the WebSocket boundary without the overhead of JSON stringification for binary data.

## Broadcasting Real-Time Events

The `SocketBroadcaster` enables the server to push updates to all connected clients through a thin API surface. Key events include:

- `test:started` and `test:queued` for test lifecycle management
- `command:log` and `command:end` for live command logging in the UI
- `network:stub` and `network:request` for network interception updates

This broadcast mechanism powers the real-time updates visible in the Cypress Test Runner, including automatic retry notifications and live DOM snapshots.

## Practical Implementation Examples

The following examples demonstrate how Cypress packages interact with the socket layer:

```typescript
// Client-side: Creating a socket connection from the driver
import { createWebsocket } from '@packages/socket/lib/client'

const socket = createWebsocket({
  path: '/__cypress/socket',
  browserFamily: 'chromium',   // 'firefox' | 'webkit' etc.
})

// Listen for test run events
socket.on('test:started', (payload) => {
  console.log('Test started →', payload.title)
})

// Emit command log entries
socket.emit('command:log', {
  id: 'c1',
  name: 'click',
  args: [{ selector: '.btn' }],
  state: 'passed',
})

```

```typescript
// Server-side: Broadcasting events from @packages/server
import { SocketBroadcaster } from '@packages/socket/lib/node/socket-broadcaster'

const broadcaster = new SocketBroadcaster()

// Notify all clients when a test is queued
broadcaster.emit('test:queued', { title: 'My spec' })

// Forward network stub updates to browsers
broadcaster.emit('network:stub', {
  requestId: 'r123',
  fixture: 'users.json',
})

```

## Summary

- Cypress centralizes WebSocket functionality in the `@packages/socket` package, which wraps socket.io with environment-specific customizations.
- **Chromium browsers** use `CDPBrowserSocket` ([`packages/socket/lib/client/cdp-browser.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/client/cdp-browser.ts)) stored on `window.cypressSockets` to persist connections across page reloads.
- **WebKit browsers** fall back to **polling** transport due to known socket.io compatibility issues, while other browsers use native **WebSocket**.
- The **`cypressParser`** (`packages/socket/patches/socket.io-parser+4.2.6.patch`) provides a custom serialization format for Cypress-specific payloads.
- **`SocketBroadcaster`** ([`packages/socket/lib/node/socket-broadcaster.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/node/socket-broadcaster.ts)) enables the server to emit events to all connected clients for real-time UI updates.
- Namespaces isolate concurrent test runs, and the dual client/server architecture ensures bidirectional communication between the test runner, driver, and browser.

## Frequently Asked Questions

### How does Cypress maintain WebSocket connections across page reloads?

For Chromium-based browsers, Cypress stores the `CDPBrowserSocket` instance on the global `window.cypressSockets` object. When the application under test reloads (common when testing authenticated routes), the client code checks for existing socket instances in this namespace before creating new connections. This prevents connection drops that would otherwise interrupt test execution during navigation events.

### Why does Cypress use different transports for different browsers?

Cypress automatically selects WebSocket transport for most browsers but forces **polling** for WebKit (Safari) because the socket.io WebSocket transport implementation is broken in those environments. The transport selection logic in [`packages/socket/lib/client/browser.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/client/browser.ts) evaluates the `browserFamily` parameter and sets `transports: ['websocket']` or `transports: ['polling']` accordingly to ensure reliable communication across all supported browsers.

### What is the purpose of the custom cypressParser in Cypress WebSocket handling?

The `cypressParser` is a patched version of the socket.io parser located in `packages/socket/patches/socket.io-parser+4.2.6.patch`. It handles serialization of Cypress-specific data structures—such as command IDs, test state objects, and network request metadata—while preserving binary efficiency. Both client and server use this parser to maintain wire format compatibility and avoid JSON serialization overhead for binary data like DOM snapshots.

### How does the SocketBroadcaster work in the Cypress real-time architecture?

The `SocketBroadcaster` class ([`packages/socket/lib/node/socket-broadcaster.ts`](https://github.com/cypress-io/cypress/blob/main/packages/socket/lib/node/socket-broadcaster.ts)) provides a server-side API to emit events to all connected socket.io clients simultaneously. When the Cypress server detects events like test starts, command completions, or network stubbing updates, it uses the broadcaster to push these events to every connected browser and UI client, enabling the live updates seen in the Test Runner dashboard.