Secure WebSocket Traffic
WebSocket provides persistent, bidirectional communication over a single TCP connection. After the initial connection is established, the client and server can exchange messages without creating a new HTTP request for every interaction. This makes WebSocket suitable for live feeds, chat, collaborative applications, multiplayer games, and other workloads that require low-latency updates.
Each WebSocket connection begins as an HTTP upgrade request. The gateway must make its authentication decision during this handshake, before the connection becomes a long-lived communication channel. APISIX can apply an authentication plugin to the upgrade request and reject unauthorized clients before allowing the protocol switch.
This guide configures the key-auth plugin and uses websocat, a command-line client that can add a credential header to the handshake, to verify both rejected and authenticated connections. Browser WebSocket APIs do not allow applications to set arbitrary request headers, so browser applications should use an authentication method and credential transport that fit their client architecture. The same handshake can use another APISIX authentication plugin when it better matches the client and application.
Prerequisite(s)
- Install Docker.
- Install cURL to configure APISIX through the Admin API.
- Install websocat to establish WebSocket connections.
- Follow the Getting Started tutorial to start an APISIX instance in Docker.
- If you use the Admin API examples and authentication is enabled, export an admin-role key as
ADMIN_API_KEY. Configure the key underdeployment.admin.admin_key. The Docker quickstart disables authentication for local evaluation; keep it enabled in production.
Start a WebSocket Upstream
Set GATEWAY_CONTAINER to the running APISIX container. Create a dedicated network and connect the gateway to it:
export GATEWAY_CONTAINER=replace-with-apisix-container-name
docker network create gateway-websocket-net
docker network connect gateway-websocket-net "$GATEWAY_CONTAINER"
Start a pinned sample WebSocket server on the shared network:
docker run -d \
--name websocket-server \
--network gateway-websocket-net \
jmalloc/echo-server:v0.3.7
The server exposes /.ws, which echoes each received message.
Create a Route
Create a route that enables WebSocket proxying and requires key-auth credentials:
- Admin API
- ADC
curl "http://127.0.0.1:9180/apisix/admin/routes/websocket-auth" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"uri": "/.ws",
"enable_websocket": true,
"plugins": {
"key-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"websocket-server:8080": 1
}
}
}'
services:
- name: websocket-auth
labels:
docs-example: secure-websocket-traffic
routes:
- name: websocket-auth
uris:
- /.ws
enable_websocket: true
plugins:
key-auth: {}
upstream:
type: roundrobin
nodes:
- host: websocket-server
port: 8080
weight: 1
Create a Consumer
Create a consumer named john with a key-auth credential:
- Admin API
- ADC
Create the consumer:
curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"username": "john"
}'
Create the credential:
curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "john-key-auth",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'
consumers:
- username: john
labels:
docs-example: secure-websocket-traffic
credentials:
- name: john-key-auth
type: key-auth
config:
key: john-key
ADC reconciles services and consumers as desired state. Preview the changes owned by this example and confirm that the diff contains no unintended updates or deletions:
adc diff -f route.yaml -f consumer.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=secure-websocket-traffic
Synchronize the reviewed service and consumer configurations:
adc sync -f route.yaml -f consumer.yaml \
--include-resource-type service \
--include-resource-type consumer \
--label-selector docs-example=secure-websocket-traffic
Verify Authentication
Open the route without credentials:
websocat "ws://127.0.0.1:9080/.ws"
APISIX should reject the handshake with 401 Unauthorized.
Open the route with John's credential:
websocat "ws://127.0.0.1:9080/.ws" -H "apikey: john-key"
Send hello. The server should echo the message:
Request served by <container-id>
hello
hello
The successful upgrade and echoed message confirm that APISIX authenticated the handshake and proxies traffic in both directions.
Next Steps
You have configured APISIX to authenticate WebSocket upgrade requests. To control the number of concurrent connections, see Rate Limit WebSocket Connections.