Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Is it possible to configure pound to handle WebSocket requests? If not, what are best alternatives for a reverse proxy? I'd like to use pound or a equivalent light-weight reverse proxy.

share|improve this question
Doesn't it support websockets already? Websockets mimic normal HTTP communication so that existing proxies can handle them. – Jan Dvorak Jan 26 '13 at 21:18
@JanDvorak - Pound may have support, but it doesn't seem to work. In my experiments (which consisted of tcpdump captures on either side of the proxy), it appeared that Pound allowed the protocol upgrade, sent the first few bytes of payload back to the client, then simply stopped transmitting. I didn't experiment beyond determining that it doesn't work. I needed a solution, not a romp through Pound's source code. :-P – ghoti Jul 4 '13 at 6:49
up vote 1 down vote accepted

Pound appears to include code that supports the protocol upgrade, but I've never been able to make it work. Nor have various folks in forums and on the pound mailing list.

There's a pretty detailed post at that describes a number of options for load balancing websockets behind SSL, including Pound (which the author also eventually gave up on). The conclusion of this post (which dates from early 2012) is that there is no good solution.

Since that post, nginx may have added websocket proxy support, so that's worth looking at. nginx is a bit more involved in terms of configuration, and IIRC has some limitations with regard to sticky session management, but it is a reliable, fast reverse proxy that supports SSL.

If you don't require SSL for your websocket connections, you might want to try a simple TCP load balancer. There are many to choose from -- HAProxy is well loved by Linux folks, but simple, high quality alternatives exist like Pen, OpenBSD's relayd (or its FreeBSD port), etc.

If you only need a reverse proxy in front of a single back-end server, and don't need to load balance, you can probably just use stunnel to receive front-end HTTPS/WSS connections and connect to an internal back-end. Here's some sample stunnel configuration. Alternately, you might be able to use stunnel in front of pen, but you'd have to experiment -- I haven't done that, and can't tell you if it'll work. (If you try, please let us know your results!)


HAProxy 1.5.0 was released on June 19th, 2014. This version includes native SSL support on both sides of the connection, which means that this is now my "preferred" solution for a WebSocket proxy. Configuration is incredibly easy:

frontend http-in
    bind     # if you want
    bind ssl crt /etc/ssl/yadda.pem
    use_backend ws if { hdr(Upgrade) -i WebSocket }

backend ws
    server node1
    server node2

Or alternately, you could do this via hostname using an ACL:

frontend http-in
    acl is_ws hdr_end(host) -i
    use_backend ws if is_ws
share|improve this answer

@ghoti's answer worked well and I will probably stick to using stunnel as suggested but this issue nagged me nevertheless so I will expand on the comment of @JanDvorak who claimed having done some experiments without going into further detail.

I used the following simple python websocket server which is derived from

import struct
import SocketServer
from base64 import b64encode
from hashlib import sha1
from mimetools import Message
from StringIO import StringIO

class WebSocketsHandler(SocketServer.StreamRequestHandler):
    magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

    def setup(self):
        print "connection established", self.client_address

    def handle(self):
        data = self.request.recv(1024).strip()
        headers = Message(StringIO(data.split('\r\n', 1)[1]))
        if headers.get("Upgrade", None) != "websocket":
        print 'Handshaking...'
        key = headers['Sec-WebSocket-Key']
        digest = b64encode(sha1(key + self.magic).hexdigest().decode('hex'))
        response = 'HTTP/1.1 101 Switching Protocols\r\n'
        response += 'Upgrade: websocket\r\n'
        response += 'Connection: Upgrade\r\n'
        response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest

        length = ord([1]) & 127
        if length == 126:
            length = struct.unpack(">H",[0]
        elif length == 127:
            length = struct.unpack(">Q",[0]
        masks = [ord(byte) for byte in]
        decoded = ""
        for char in
            decoded += chr(ord(char) ^ masks[len(decoded) % 4])

        print decoded

        length = len(decoded)
        if length <= 125:
        elif length >= 126 and length <= 65535:
            self.request.send(struct.pack(">H", length))
            self.request.send(struct.pack(">Q", length))


if __name__ == "__main__":
    server = SocketServer.TCPServer(
        ("localhost", 9000), WebSocketsHandler)
    except KeyboardInterrupt:
        print "Got ^C"
        print "bye!"

And I combined it with the following html which was borrowed from

<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
  var wsUri = "ws://localhost:9000/";
  var output;
  function init() {
    output = document.getElementById("output");
  function testWebSocket() {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  function onOpen(evt) {
    doSend("WebSocket rocks");
  function onClose(evt) {
  function onMessage(evt) {
    writeToScreen('<span style="color: blue;">RESPONSE: ' +'</span>');
  function onError(evt) {
    writeToScreen('<span style="color: red;">ERROR:</span> ' +;
  function doSend(message) {
    writeToScreen("SENT: " + message); 
  function writeToScreen(message) {
    var pre = document.createElement("p"); = "break-word";
    pre.innerHTML = message;
  window.addEventListener("load", init, false);
<h2>WebSocket Test</h2>
<div id="output"></div>

This worked well, so I put pound in the middle with the following config entry:

    Port    9999
                    Port    9000

and changed the port in the html from 9000 to 9999. After that change it stopped working.

Analyzing the traffic with wireshark, I found out that the HTTP 101 request to switch the protocol gets forwarded correctly. But the subsequent first websocket packet never gets forwarded by pound. This is confirmed by the print output of the python server script which never receives the WebSocket rocks message with pound in the middle.

Whenever pound receives a WebSocket message, it drops the message and instead writes e414 headers: request URI too long to syslog. Looking at the pound source code this seems to be because pound tries to parse HTTP headers. To do this it first searches for an EOL which it can't find in the WebSocket message and thus drops the message as invalid.

So it seems that the answer to OP's question indeed is: pound can't do WebSocket.

I wrote an email to the pound list about this issue:

share|improve this answer
Nicely done. Thanks for this update. :-) – ghoti Sep 17 '14 at 15:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .