---
title: WebSockets passthrough
summary: null
url: >-
  https://www.fastly.com/documentation/guides/concepts/real-time-messaging/websockets-tunnel
---

[WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) are two-way communication channels between a client device (such as a web browser) and a server, allowing the server to send messages to the client at any time without the client having to make a request.

Unlike the HTTP requests that make up the bulk of web traffic handled by Fastly, WebSockets are long-lived connections and do not have a request-response cycle. They instead can carry data in either direction at any time. As a result, WebSockets don't fit Fastly's normal processing model for edge traffic, but because WebSocket connections begin life as HTTP requests, you can pass them directly to the origin server.

> **HINT:** WebSockets passthrough creates one WebSocket connection to origin for every connection from a client device to Fastly. To have Fastly broker messages for you with channels and one-to-many publishing, consider using [Fanout](https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout) instead.

## Limitations and considerations

When using WebSockets, keep in mind the following limitations and considerations:

- WebSockets is not compatible with [shielding](https://www.fastly.com/documentation/guides/getting-started/hosts/shielding) or the [Fastly Next-Gen WAF](https://docs.fastly.com/products/fastly-next-gen-waf).
- When [adding a host](https://www.fastly.com/documentation/guides/getting-started/hosts/working-with-hosts) to your Fastly service configuration, only the **Name**, **Address**, **Enable TLS**, and **Override Host** origin server settings are supported with WebSockets.
- When handling a WebSocket request, `vcl_log` will run at the time the request is accepted rather than when the connection ends.
- Client request headers that are added, removed, or modified on your `Request` (or `req.http` in VCL) will be reflected in the WebSocket handoff.
- If you have TLS certificates on your origin server, they must be signed by a public certification authority. Self-signed TLS certificates are not supported.

## Enabling WebSockets passthrough

<div id="enabling-websocket-passthrough" />

WebSockets passthrough is an [optional upgrade](https://docs.fastly.com/products/websockets) to Fastly service plans. To use WebSockets passthrough, you need a paid Fastly account.

Once your Fastly account has full access to WebSockets, it can be enabled on an individual service in the Fastly control panel or by enabling the WebSockets product using the [Products API for WebSockets](https://www.fastly.com/documentation/reference/api/products/websockets/).

> **WARNING:** Enabling or disabling WebSockets immediately impacts all service versions, including the active one.

### Cdn Services

In a VCL service, your VCL is invoked for each inbound client request and [the VCL workflow](https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl) is designed to manage a conventional request-response cycle. To handle the WebSocket connection, you must hand off the request from VCL so that Fastly can continue to hold the connection and relay traffic in both directions. To do this, `return(upgrade)` from `vcl_recv`:

```vcl context="sub vcl_recv { ... }"
if (req.http.Upgrade) {
    return (upgrade);
}
```

### Compute Services

Compute programs are typically invoked for each client request and end when you deliver a response. To handle the WebSocket connection, the request must be handed off from the Compute program so that Fastly can continue to hold the connection and relay traffic in both directions.

### Rust

This is performed by the [`handoff_websocket`](https://docs.rs/fastly/latest/fastly/struct.Request.html#method.handoff_websocket) method on the `Request` struct. If you are expecting your service to handle more than just WebSockets traffic, it's a good idea to only do this when the request has an `Upgrade: websocket` header:

```rust
use fastly::{Error, Request};

fn main() -> Result<(), Error> {
    let req = Request::from_client();

    if let Some("websocket") = req.get_header_str("Upgrade") {
        return Ok(req.handoff_websocket("ws_backend_name")?);
    }

    Ok(req.send("non_ws_backend_name")?.send_to_client())
}
```

> **TIP:** You can create a Rust-based service, automatically populated with the code needed to perform WebSockets passthrough, using <kbd>fastly compute init</kbd>:
>
> ```term
> $ fastly compute init --from=https://github.com/fastly/compute-starter-kit-rust-websockets
> $ fastly compute publish
> ```

### Javascript

This is performed by the [`createWebsocketHandoff`](https://js-compute-reference-docs.edgecompute.app/docs/fastly:websocket/createWebsocketHandoff) function exported from the `'fastly:websocket'` module. If you are expecting your service to handle more than just WebSockets traffic, it's a good idea to only do this when the request has an `Upgrade: websocket` header:

```javascript

async function handleRequest(event) {
  let req = event.request;
  if (req.headers.get('Upgrade') === 'websocket') {
    return createWebsocketHandoff(event.request, 'ws_backend_name');
  } else {
    return await fetch(req, {backend: "non_ws_backend_name"});
  }
}

addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
```

> **TIP:** You can create a JavaScript-based service, automatically populated with the code needed to perform WebSockets passthrough, using <kbd>npm init</kbd>:
>
> ```term
> $ npm init @fastly/compute@latest -- --language=javascript --starter-kit=websockets
> $ npm run deploy
> ```

### Go

This is performed by the [`Websocket`](https://pkg.go.dev/github.com/fastly/compute-sdk-go/x/exp/handoff#Websocket) function exported from the `github.com/fastly/compute-sdk-go/x/exp/handoff` package. If you are expecting your service to handle more than just WebSockets traffic, it's a good idea to only do this when the request has an `Upgrade: websocket` header:

```go
package main

import (
	"context"
	"io"

	"github.com/fastly/compute-sdk-go/fsthttp"
	"github.com/fastly/compute-sdk-go/x/exp/handoff"
)

func main() {
	fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
		if r.Header.Get("Upgrade") == "websocket" {
            handoff.Websocket("ws_backend_name")
        } else {
            resp, _ := r.Send(ctx, "non_ws_backend_name")
            w.Header().Reset(resp.Header)
            w.WriteHeader(resp.StatusCode)
            io.Copy(w, resp.Body)
        }
	})
}
```

> **TIP:** You can create a Go-based service, automatically populated with the code needed to perform WebSockets passthrough, using <kbd>fastly compute init</kbd>:
>
> ```term
> $ fastly compute init --from=https://github.com/fastly/compute-starter-kit-go-websockets
> $ fastly compute publish
> ```

### Cpp

In this version of the Fastly Compute C++ SDK, WebSockets passthrough is not available.

## Tips

The following tips and best practices may help you get the most out of WebSockets passthrough:

- If you use the web interface or API to create the backend, be sure to set a **host header override** if your server's hosting is name-based. [Learn more](https://www.fastly.com/documentation/guides/integrations/non-fastly-services/developer-guide-backends/#creating-backends).
- Unlike [most Rust-based Compute programs](https://www.fastly.com/documentation/guides/compute/developer-guides/rust/#main-interface), you cannot use the `#[fastly::main]` macro in a program that does `handoff_websocket`. This is because `handoff_websocket` will immediately start a response to the client, making it impossible to return a `Response` from the `main()` function without causing an error.
- Client request headers that are added, removed, or modified on your `Request` (or `req.http` in VCL) will be reflected in the WebSocket handoff.
- Once handed off, WebSocket connections are not subject to the [`between_bytes_timeout`](https://www.fastly.com/documentation/reference/api/services/backend/#field_between_bytes_timeout) and will only drop when either the client or server disconnects. If either the client or server disconnects, Fastly will relay that disconnect to the other party.
