Proxy websocket request in nginx

1 minute read

In my recent webrtc project, websocket is needed for the peer-connection establishment process. Things worked locally, but when I deployed to production environment, it broke. Turns out that some extra configuration is needed for nginx reverse-proxy. This article will briefly introduce websocket and websocket proxying on nginx.

What is Websocket?

Websocket is an application-level communication protocol featuring full-duplex communication. Unlike http where communication is based on request-reply pattern, in websocket channel both side can send data simultaneously.

Use cases of websocket

One typical use case is pushing notification. In the past this is done by polling. Client side keeps polling server to see whether something new is coming. With websocket, server only needs to push data to client when necessary. This saves significant server resource.

How is websocket implemented?

Websocket is designed to work over 443/80 to make it compatible with http, thus no extra firewall configuration is needed.
Compared to HTTP, there is an extra handshake process to establish websocket connection. After handshake is completed, client and server can send data to each other in full-duplex mode.

Handshake process

1. Client sends handshake request

Client uses the header field Upgrade to indicate this is a websocket request.
Sample request:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade

2. Server returns handshake response

Server will response with http status code 101 indicating protocol will be switched(to websocket).
Sample response:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

Why simple proxy_pass doesn’t work?

From the handshake process we can see that the key field is Upgrade. This is a new field introduced in HTTP1.1. In RFC2616 it is defined as a hop-by-hop header where it won’t be passed with proxy. Thus, we have to manually set proxy header in nginx config if we would like to propagate the websocket request.

Sample nginx configuration

Following is the working nginx configuration for websocket:

location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Explanation

  1. Since Upgrade header is introduced in http 1.1, we need to explicitly specify the version;
  2. The $http_upgrade is deduced from client request’s upgrade header, thus it would be “websocket”.

Categories:

Updated:

Comments