Skip to main content

Proxy WebSocket Connections

WebSocket provides bidirectional communication between a client and server over a long-lived connection. When the Ingress Controller translates a route, the generated gateway route must allow the HTTP connection to upgrade to WebSocket.

The gateway can proxy WebSocket backends configured through HTTPRoute, ApisixRoute, or Ingress. To limit concurrent WebSocket connections, see the limit-conn plugin.

Prerequisites

  1. Complete Set Up Ingress Controller and Gateway.
  2. Install websocat to verify the WebSocket connection.

Enable WebSocket

Where you enable WebSocket depends on the route resource:

ResourceConfiguration
HTTPRouteSet the backend Service port appProtocol to kubernetes.io/ws or kubernetes.io/wss. HTTPRoute has no route-level alternative.
ApisixRouteSet spec.http[].websocket to true, or use the Service port appProtocol.
IngressSet the k8s.apisix.apache.org/enable-websocket annotation to true, or use the Service port appProtocol.

The examples below configure HTTPRoute and ApisixRoute. If you cannot modify the backend Service, use the ApisixRoute field or Ingress annotation instead of HTTPRoute.

Start an Example Upstream Service

Create a Kubernetes manifest file for the deployment and service of a WebSocket server. The server has a WebSocket endpoint at /.ws that echoes back any message received.

ws-echo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: websocket-server
spec:
replicas: 1
selector:
matchLabels:
app: websocket-server
template:
metadata:
labels:
app: websocket-server
spec:
containers:
- name: echo-server
image: jmalloc/echo-server
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: websocket-server
spec:
selector:
app: websocket-server
ports:
- name: ws
port: 8080
targetPort: 8080
protocol: TCP
appProtocol: kubernetes.io/ws
type: ClusterIP

The HTTPRoute example requires this appProtocol setting. Without it, the gateway treats the WebSocket handshake as an ordinary HTTP request. A successful HTTP/1.1 WebSocket handshake returns 101 Switching Protocols; a normal response such as 200 OK means the connection was not upgraded. The same setting can also enable WebSocket for Ingress and ApisixRoute. The kubernetes.io/ws value selects HTTP for the upstream connection; use kubernetes.io/wss for an upstream that accepts TLS connections. See Detect Upstream Protocol with appProtocol for the complete protocol mapping.

Apply the configuration to your cluster:

kubectl apply -f ws-echo.yaml

Create a Route

Choose HTTPRoute or ApisixRoute for the example service:

ws-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: ws-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /.ws
backendRefs:
- name: websocket-server
port: 8080

HTTPRoute has no route-level WebSocket field. The Ingress Controller reads appProtocol from the referenced Service port and enables the protocol upgrade on the generated route.

Apply the configuration to your cluster:

kubectl apply -f ws-route.yaml

Verify Connections

For the APISIX local evaluation setup, expose the gateway Service on your local machine. For an API7 installation, substitute the gateway Service generated by API7 Dashboard.

kubectl port-forward svc/apisix-gateway 9080:80 &

Establish a connection with the WebSocket server through the route:

websocat "ws://127.0.0.1:9080/.ws"

Send a "hello" message in the terminal. You should see the WebSocket server echoes back the same message:

Request served by 1cd244052136
hello
hello

You can continue to send more messages and the WebSocket server will echo back any message you sent. This shows the bidirectional connection is successful and persistent.

Update or Remove a Route

Changing the backend Service endpoints affects new WebSocket connections. An established connection remains attached to its selected backend until the connection ends, so open a new connection to verify an endpoint change.

Deleting the route prevents new matching connections but does not close connections that have already completed the WebSocket upgrade. Those connections remain open until the client, backend, or gateway closes them.