---
title: Using the WebSockets API
description: Use the WebSockets API to communicate in real time with your Cloudflare Workers.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/workers/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

### Tags

[ WebSockets ](https://developers.cloudflare.com/search/?tags=WebSockets)[ JavaScript ](https://developers.cloudflare.com/search/?tags=JavaScript)[ Rust ](https://developers.cloudflare.com/search/?tags=Rust) 

# Using the WebSockets API

**Last reviewed:**  about 5 years ago 

Use the WebSockets API to communicate in real time with your Cloudflare Workers.

WebSockets allow you to communicate in real time with your Cloudflare Workers serverless functions. In this guide, you will learn the basics of WebSockets on Cloudflare Workers, both from the perspective of writing WebSocket servers in your Workers functions, as well as connecting to and working with those WebSocket servers as a client.

WebSockets are open connections sustained between the client and the origin server. Inside a WebSocket connection, the client and the origin can pass data back and forth without having to reestablish sessions. This makes exchanging data within a WebSocket connection fast. WebSockets are often used for real-time applications such as live chat and gaming.

Note

WebSockets utilize an event-based system for receiving and sending messages, much like the Workers runtime model of responding to events.

Note

If your application needs to coordinate among multiple WebSocket connections, such as a chat room or game match, you will need clients to send messages to a single-point-of-coordination. Durable Objects provide a single-point-of-coordination for Cloudflare Workers, and are often used in parallel with WebSockets to persist state over multiple clients and connections. In this case, refer to [Durable Objects](https://developers.cloudflare.com/durable-objects/) to get started, and prefer using the Durable Objects' extended [WebSockets API](https://developers.cloudflare.com/durable-objects/best-practices/websockets/).

## Write a WebSocket Server

WebSocket servers in Cloudflare Workers allow you to receive messages from a client in real time. This guide will show you how to set up a WebSocket server in Workers.

A client can make a WebSocket request in the browser by instantiating a new instance of `WebSocket`, passing in the URL for your Workers function:

JavaScript

```

// In client-side JavaScript, connect to your Workers function using WebSockets:

const websocket = new WebSocket(

  "wss://example-websocket.signalnerve.workers.dev",

);


```

Note

For more details about creating and working with WebSockets in the client, refer to [Writing a WebSocket client](#write-a-websocket-client).

When an incoming WebSocket request reaches the Workers function, it will contain an `Upgrade` header, set to the string value `websocket`. Check for this header before continuing to instantiate a WebSocket:

* [  JavaScript ](#tab-panel-9398)
* [  Rust ](#tab-panel-9399)

JavaScript

```

async function handleRequest(request) {

  const upgradeHeader = request.headers.get('Upgrade');

  if (!upgradeHeader || upgradeHeader !== 'websocket') {

    return new Response('Expected Upgrade: websocket', { status: 426 });

  }

}


```

```

use worker::*;


#[event(fetch)]

async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {

    let upgrade_header = match req.headers().get("Upgrade") {

        Some(h) => h.to_str().unwrap(),

        None => "",

    };

    if upgrade_header != "websocket" {

        return worker::Response::error("Expected Upgrade: websocket", 426);

    }

}


```

After you have appropriately checked for the `Upgrade` header, you can create a new instance of `WebSocketPair`, which contains server and client WebSockets. One of these WebSockets should be handled by the Workers function and the other should be returned as part of a `Response` with the [101 status code ↗](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/101), indicating the request is switching protocols:

* [  JavaScript ](#tab-panel-9400)
* [  Rust ](#tab-panel-9401)

JavaScript

```

async function handleRequest(request) {

  const upgradeHeader = request.headers.get('Upgrade');

  if (!upgradeHeader || upgradeHeader !== 'websocket') {

    return new Response('Expected Upgrade: websocket', { status: 426 });

  }


  const webSocketPair = new WebSocketPair();

  const client = webSocketPair[0],

    server = webSocketPair[1];


  return new Response(null, {

    status: 101,

    webSocket: client,

  });

}


```

```

use worker::*;


#[event(fetch)]

async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {

    let upgrade_header = match req.headers().get("Upgrade") {

        Some(h) => h.to_str().unwrap(),

        None => "",

    };

    if upgrade_header != "websocket" {

        return worker::Response::error("Expected Upgrade: websocket", 426);

    }


    let ws = WebSocketPair::new()?;

    let client = ws.client;

    let server = ws.server;

    server.accept()?;


    worker::Response::from_websocket(client)


}


```

The `WebSocketPair` constructor returns an Object, with the `0` and `1` keys each holding a `WebSocket` instance as its value. It is common to grab the two WebSockets from this pair using [Object.values ↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5Fobjects/Object/values) and [ES6 destructuring ↗](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring%5Fassignment), as seen in the below example.

In order to begin communicating with the `client` WebSocket in your Worker, call `accept` on the `server` WebSocket. This will tell the Workers runtime that it should listen for WebSocket data and keep the connection open with your `client` WebSocket:

* [  JavaScript ](#tab-panel-9402)
* [  Rust ](#tab-panel-9403)

JavaScript

```

async function handleRequest(request) {

  const upgradeHeader = request.headers.get('Upgrade');

  if (!upgradeHeader || upgradeHeader !== 'websocket') {

    return new Response('Expected Upgrade: websocket', { status: 426 });

  }


  const webSocketPair = new WebSocketPair();

  const [client, server] = Object.values(webSocketPair);


  server.accept();


  return new Response(null, {

    status: 101,

    webSocket: client,

  });

}


```

```

use worker::*;


#[event(fetch)]

async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {

    let upgrade_header = match req.headers().get("Upgrade") {

        Some(h) => h.to_str().unwrap(),

        None => "",

    };

    if upgrade_header != "websocket" {

        return worker::Response::error("Expected Upgrade: websocket", 426);

    }


    let ws = WebSocketPair::new()?;

    let client = ws.client;

    let server = ws.server;

    server.accept()?;


    worker::Response::from_websocket(client)


}


```

WebSockets emit a number of [Events](https://developers.cloudflare.com/workers/runtime-apis/websockets/#events) that can be connected to using `addEventListener`. The below example hooks into the `message` event and emits a `console.log` with the data from it:

* [  JavaScript ](#tab-panel-9404)
* [  Rust ](#tab-panel-9405)
* [  Hono ](#tab-panel-9406)

JavaScript

```

async function handleRequest(request) {

  const upgradeHeader = request.headers.get('Upgrade');

  if (!upgradeHeader || upgradeHeader !== 'websocket') {

    return new Response('Expected Upgrade: websocket', { status: 426 });

  }


  const webSocketPair = new WebSocketPair();

  const [client, server] = Object.values(webSocketPair);


  server.accept();

  server.addEventListener('message', event => {

    console.log(event.data);

  });


  return new Response(null, {

    status: 101,

    webSocket: client,

  });

}


```

```

use futures::StreamExt;

use worker::*;


#[event(fetch)]

async fn fetch(req: HttpRequest, _env: Env, _ctx: Context) -> Result<worker::Response> {

    let upgrade_header = match req.headers().get("Upgrade") {

        Some(h) => h.to_str().unwrap(),

        None => "",

    };

    if upgrade_header != "websocket" {

        return worker::Response::error("Expected Upgrade: websocket", 426);

    }


    let ws = WebSocketPair::new()?;

    let client = ws.client;

    let server = ws.server;

    server.accept()?;


    wasm_bindgen_futures::spawn_local(async move {

        let mut event_stream = server.events().expect("could not open stream");

        while let Some(event) = event_stream.next().await {

            match event.expect("received error in websocket") {

                WebsocketEvent::Message(msg) => server.send(&msg.text()).unwrap(),

                WebsocketEvent::Close(event) => console_log!("{:?}", event),

            }

        }

    });

    worker::Response::from_websocket(client)


}


```

TypeScript

```

import { Hono } from 'hono'

import { upgradeWebSocket } from 'hono/cloudflare-workers'


const app = new Hono()


app.get(

  '*',

  upgradeWebSocket((c) => {

    return {

      onMessage(event, ws) {

        console.log('Received message from client:', event.data)

        ws.send(`Echo: ${event.data}`)

      },

      onClose: () => {

        console.log('WebSocket closed:', event)

      },

      onError: () => {

        console.error('WebSocket error:', event)

      },

    }

  })

)


export default app;


```

### Connect to the WebSocket server from a client

Writing WebSocket clients that communicate with your Workers function is a two-step process: first, create the WebSocket instance, and then attach event listeners to it:

JavaScript

```

const websocket = new WebSocket(

  "wss://websocket-example.signalnerve.workers.dev",

);

websocket.addEventListener("message", (event) => {

  console.log("Message received from server");

  console.log(event.data);

});


```

WebSocket clients can send messages back to the server using the [send](https://developers.cloudflare.com/workers/runtime-apis/websockets/#send) function:

JavaScript

```

websocket.send("MESSAGE");


```

When the WebSocket interaction is complete, the client can close the connection using [close](https://developers.cloudflare.com/workers/runtime-apis/websockets/#close):

JavaScript

```

websocket.close();


```

For an example of this in practice, refer to the [websocket-template ↗](https://github.com/cloudflare/websocket-template) to get started with WebSockets.

## Write a WebSocket client

Cloudflare Workers supports the `new WebSocket(url)` constructor. A Worker can establish a WebSocket connection to a remote server in the same manner as the client implementation described above.

Additionally, Cloudflare supports establishing WebSocket connections by making a fetch request to a URL with the `Upgrade` header set.

JavaScript

```

async function websocket(url) {

  // Make a fetch request including `Upgrade: websocket` header.

  // The Workers Runtime will automatically handle other requirements

  // of the WebSocket protocol, like the Sec-WebSocket-Key header.

  let resp = await fetch(url, {

    headers: {

      Upgrade: "websocket",

    },

  });


  // If the WebSocket handshake completed successfully, then the

  // response has a `webSocket` property.

  let ws = resp.webSocket;

  if (!ws) {

    throw new Error("server didn't accept WebSocket");

  }


  // Call accept() to indicate that you'll be handling the socket here

  // in JavaScript, as opposed to returning it on to a client.

  // You can pass { allowHalfOpen: true } if you need to coordinate

  // the close handshake manually (for example, when proxying).

  ws.accept();


  // Now you can send and receive messages like before.

  ws.send("hello");

  ws.addEventListener("message", (msg) => {

    console.log(msg.data);

  });

}


```

## WebSocket close behavior

With the [web\_socket\_auto\_reply\_to\_close](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#websocket-auto-reply-to-close) compatibility flag (enabled by default on compatibility dates on or after `2026-04-07`), the Workers runtime automatically replies to incoming Close frames and transitions `readyState` to `CLOSED` before firing the `close` event. You do not need to call `close()` in your `close` event handler, but doing so is safe (the call is silently ignored).

If you need half-open behavior (for example, for WebSocket proxying), pass `{ allowHalfOpen: true }` to `accept()`. Note that `new WebSocket(url)` always auto-replies after this flag takes effect. To get half-open behavior for a client WebSocket, use the `fetch()`\-based pattern shown above and call `ws.accept({ allowHalfOpen: true })`.

For more details, refer to [WebSocket close behavior](https://developers.cloudflare.com/workers/runtime-apis/websockets/#close-behavior).

## WebSocket compression

Cloudflare Workers supports WebSocket compression. Refer to [WebSocket Compression](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#websocket-compression) for more information.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/workers/","name":"Workers"}},{"@type":"ListItem","position":3,"item":{"@id":"/workers/examples/","name":"Examples"}},{"@type":"ListItem","position":4,"item":{"@id":"/workers/examples/websockets/","name":"Using the WebSockets API"}}]}
```
