WebSockets passthrough

The guidance on this page was tested with an older version (0.10.4) of the Rust SDK. It may still work with the latest version (0.11.2), but the change log may help if you encounter any issues.

WebSockets 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 which make up the bulk of web traffic handled by Fastly, WebSockets are long-lived connections and do not have a request-response cycle, but 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 since WebSocket connections begin life as HTTP requests, you can pass the WebSocket connection directly to the origin server.

HINT: WebSocket 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 instead.

Enabling WebSocket passthrough

WebSockets passthrough is an optional upgrade to Fastly service plans. If you have not yet purchased access, contact sales, or start a free trial by enabling the toggle in the web interface on any service.

If your Fastly account has full access to WebSockets, it can be enabled on an individual service in the web interface or by enabling the websockets product using the product enablement API.

To use WebSocket passthrough, you need a paid Fastly account containing a VCL service, or either a Rust-based or a Go-based Compute service. A free trial can be enabled in the 'WebSockets' section of the service configuration options in the web interface.

Fastly Compute

You can create a Rust-based service, automatically populated with the code needed to perform WebSocket passthrough, using fastly compute init:

$ fastly compute init --from=https://github.com/fastly/compute-starter-kit-rust-websockets
$ fastly compute publish

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.

This is performed by the 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:

use fastly::{Error, RequestHandle};
fn main() -> Result<(), Error> {
let req = RequestHandle::from_client();
if let Some("websocket") = req.get_header_value("Upgrade") {
return Ok(req.handoff_websocket("ws_backend_name")?);
}
Ok(req.send("non_ws_backend_name")?.send_to_client())
}

If you prefer to use Go instead of Rust, you can do it with handoff.Websocket() method.

VCL

In a VCL service, your VCL is invoked for each inbound client request and the VCL workflow 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:

sub vcl_recv { ... }
Fastly VCL
if (req.http.Upgrade) {
return (upgrade);
}

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, ensure to set a host header override if your server's hosting is name-based. Learn more.
  • Unlike most Rust-based Compute programs, 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.
  • WebSocket connections, once handed off, are not subject to the 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.