Technology: Reverse WebSocket proxy

Many modern web applications use not only HTTP and HTTPS, but also WebSocket (RFC 6455) for data transfer. For example, a web application frontend can work through HTTPS, and a video stream can be loaded through WebSocket. WebSocket upstreams are often located on the same IP addresses or networks as HTTP upstreams, so unprotected WebSocket traffic can make it easy for an attacker to attack your network even when HTTP traffic is fully protected.

Therefore, clients developing such multi-protocol web applications should ensure that all application traffic passes through the Qrator Labs network. To do this, you should use a combination of reverse HTTP proxy and reverse WebSocket proxy.

For information on how to start using the Qrator Labs reverse WebSocket proxy, see Connection: Reverse WebSocket Proxy.

How it works

Part of the WebSocket protocol is the handshake, which the user and the upstream must complete before data transfer can begin. The handshake itself uses the HTTP protocol. After the handshake, both parties can send WebSocket traffic to each other until the connection is closed by one of the parties.

The reverse WebSocket proxy proxies the user request and the server response, with additional HTTP headers added to the request. The reverse proxy then accepts and proxies WebSocket traffic both ways through the connections established during handshake.

It is the responsibility of the application to parse and process the data transferred after the handshake. The reverse proxy does not parse or modify WebSocket traffic, which allows it to be used regardless of the format of the data being sent.

Below is a detailed description of the handshake procedure involving a reverse WebSocket proxy.

  1. The user sends an HTTP 101 Switching Protocols request.

    Upon receiving this request, the reverse proxy checks if the number of requests from the user's IP address exceeds the client-specified limit. If the limit is not exceeded yet, the reverse proxy selects upstream for this user based on his IP address. See Balancing.

  2. An HTTP 101 Switching Protocols request is proxied to the selected upstream with the headers the user specified in his request as well as additional HTTP headers.

    Warning

    By default, the reverse proxy is configured to remove headers containing an underscore _ before passing the request to the upstream. If you need to keep such headers in the request, contact Qrator Labs technical support to change this setting.

  3. The reverse proxy waits 5 seconds for a response from the upstream. If the upstream does not respond, the reverse proxy returns to step 2 and attempts to contact the client's other upstreams. Read more about this in Balancing.

    • If no response is received from any upstream, the reverse proxy returns an HTTP 400 Bad Request response to the user and the attempt to start the WebSocket protocol is abandoned.

    • If the upstream responds with an error message with any status, connections are dropped and the attempt to start the WebSocket protocol is abandoned.

    • If the upstream responds with a handshake with an HTTP 101 Switching Protocols status and correct headers, the reverse proxy proxies this response to the user unchanged. In this case, the connection between the reverse proxy and the upstream is considered successfully established.

  4. The user checks the status and headers of the received response. This completes the handshake, and the WebSocket connection between the user and the reverse proxy is considered successfully established. The HTTP protocol is no longer used for this connection.

All further information transmission from the user to the upstream and back takes place through the reverse proxy within the already established connection pair.

The reverse proxy does not analyze the forwarded data and does not change its content during transmission, which allows it to work correctly regardless of the format of the transmitted data.

When one party closes the connection to the reverse proxy, the reverse proxy automatically closes the connection to the other party.

expand_less