This is my second post around WebSockets this month. I’m working a lot with WebSockets these days due to my two ongoing projects WebSocket.in and Groupwat.ch which require WebSockets.

If your website is on HTTPS, you can not connect to a WebSocket server running on WS (unsecured WebSocket protocol) and hence, setting up WSS (secure WebSocket protocol) on it is a real need these days with almost every website moving to HTTPS with LetsEncrypt’s free SSL certificates.

Let’s see how can we enable WSS for this simple Ratchet application

If this code looks unfamiliar to you, please read Ratchet’s getting started tutorial.
Notice the curly braces around the route path? That’s because of  Wildcard routing in ratchet.

Setting up WSS with Apache

We assume that your web-server is Apache and you are hosting example.com with it over SSL.

With your favorite text editor (vim?) open the virtual host configuration file
for example.com over SSL, the one that listens to the port 433.

And add these two directives to it: ProxyPassMatch and ProxyPassReverse as shown below

 

<VirtualHost *:443>
.....
ProxyPassMatch ^/wss2/(.*) ws://exampl.com:12345/$1
ProxyPassReverse ^/wss2/(.*) ws://example.com:12345/$1
......
</VirtualHost>

Replace example.com and 12345 with your WebSocket’s host and protocol respectively.

My local working virtual host setup for WSS looks like this:

<VirtualHost *:443>
DocumentRoot "/Users/hack4mer/Sites"
ServerName localhost

SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM

SSLCertificateFile /usr/local/etc/httpd/certs/server.crt
SSLCertificateKeyFile /usr/local/etc/httpd/certs/server.key
SSLCertificateChainFile /usr/local/etc/httpd/certs/ca.pem

#Redirect all wss requests to the ws protocoal internally
ProxyPassMatch ^/wss2/(.*) ws://localhost:12345/$1
ProxyPassReverse ^/wss2/(.*) ws://localhost:12345/$1

<Directory /Users/hack4mer/Sites>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

After these modifications in your Apache configuration, restart the server and connect to the WebSocket server using: wss://example.com/wss2/your_path

Easy, right!? However, I faced this weird issue with the reverse proxy setup for Ratchet WebSocket server on Apache, my connections were dropping 20% of the time with browser showing 503 errors.

I was able to eradicate this issue if I connected to the socket directly over WS protocol, which means the problem was somewhere with the connection between Apache and the unsecured WebSocket server with the reverse proxy setup as shown above. That’s why I chose to give Nginx’s reverse proxy setup a try and it worked out good for me! Connections were successful 100% of the time and people using WebSocket.in could rely on it.

Setting up WSS with Nginx

Edit the server block for SSL configuration of your host example.com

And add the following to it:

 

upstream localhost{
   server example.com:12345;
}
server {
        listen       443;
        .....
        location /wss2/ {
            proxy_pass http://localhost;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_redirect off;
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
            keepalive_timeout 86400s;
            # prevents 502 bad gateway error
            proxy_buffers 8 32k;
            proxy_buffer_size 64k;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            reset_timedout_connection on;
        }
}

My working server block configuration looks like this:

upstream localhost{
   server example.com:12345;
}


server {
        listen       443;
        server_name  example.com;
        root         /var/www/html/example.com;

        ssl_certificate "/etc/letsencrypt/live/example.com/cert.pem";
        ssl_certificate_key "/etc/letsencrypt/live/example.com/privkey.pem";
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;


        location /wss2/ {
            proxy_pass http://localhost;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_redirect off;
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
            keepalive_timeout 86400s;
            # prevents 502 bad gateway error
            proxy_buffers 8 32k;
            proxy_buffer_size 64k;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            reset_timedout_connection on;
        }
}

Restart the Nginx server and connect to the WebSocket server using: wss://example.com/wss2/your_path

Bonus tip: To avoid 404 errors on the WebSocket server, Nginx’s upstream name should match the hostname provided to $server = new Ratchet\App("localhost",12345, '0.0.0.0'); as the first argument. In our case, localhost.

Hope this helps!


Also published on Medium.