WebSocket through SSL with Apache reverse proxy

30,769

Solution 1

I ended up solving this problem by using this configuration for the virtual host, which filters requests using the HTTP headers:

<VirtualHost *:443>
    ServerName website.com

    RewriteEngine On

    # When Upgrade:websocket header is present, redirect to ws
    # Using NC flag (case-insensitive) as some browsers will pass Websocket
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule ^/ws/(.*)    wss://localhost:8888/ws/$1 [P,L]

    # All other requests go to http
    ProxyPass "/" "http://localhost:8888/"

I'm leaving this as a reference in case it helps others

Solution 2

In order to place a secure reverse proxy server in front of an insecure websocket server, you could do this:

<VirtualHost *:443>
    SSLEngine on
    SSLProxyEngine on
    SSLProtocol -all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
    SSLCipherSuite HIGH:aNULL:eNULL:EXPORT:DES:RC4:!MD5:!PSK:!SRP:!CAMELLIA
    SSLCertificateFile /path/to/cert
    SSLCertificateKeyFile /path/to/key
    SSLCertificateChainFile /path/to/chain
    ServerName website.com
    
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*)    ws://localhost:8888/$1 [P,L]
</VirtualHost>

This will take a request inbound for wss://website.com:443, and reverse proxy it to ws://localhost:8888.

If the websocket server is also secure, you can simply change ws://localhost:8888 to wss://website.com:8888

Solution 3

This is my setup of virtualhost that worked for me, I have .netcore app on docker with SignalR as a websocket service.

On 5000 my .netcore app is running, and on /chatHub my signalR listens.

Will be helpful for future comers with same problem.

<IfModule mod_ssl.c>
<VirtualHost *:443>
  RewriteEngine On
  ProxyPreserveHost On
  ProxyRequests Off

  # allow for upgrading to websockets
  RewriteEngine On
  RewriteCond %{HTTP:Upgrade} =websocket [NC]
  RewriteRule /(.*)           ws://localhost:5000/$1 [P,L]
  RewriteCond %{HTTP:Upgrade} !=websocket [NC]
  RewriteRule /(.*)           http://localhost:5000/$1 [P,L]


  ProxyPass "/" "http://localhost:5000/"
  ProxyPassReverse "/" "http://localhost:5000/"

  ProxyPass "/chatHub" "ws://localhost:5000/chatHub"
  ProxyPassReverse "/chatHub" "ws://localhost:5000/chatHub"

  ServerName site.com
  
SSLCertificateFile /etc/letsencrypt/live/site.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

Source: http://shyammakwana.me/server/websockets-with-apache-reverse-proxy-with-ssl.html

Share:
30,769

Related videos on Youtube

Andrei Savin
Author by

Andrei Savin

Updated on September 22, 2021

Comments

  • Andrei Savin
    Andrei Savin over 2 years

    On the client side, I am trying to establish the wss connection:

    var ws = new WebSocket("wss://wsserver.com/test")

    and it returns an error:

    WebSocket connection to 'wss://wsserver.com/test' failed: Error during WebSocket handshake: Unexpected response code: 400

    The full headers are:

    Request Headers

    GET wss://wsserver.com/test HTTP/1.1
    Host: wsserver.com
    Connection: Upgrade
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade: websocket
    Origin: https://website.net
    Sec-WebSocket-Version: 13
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
    Accept-Encoding: gzip, deflate, sdch, br
    Accept-Language: en-US,en;q=0.8
    Sec-WebSocket-Key: Tj9AJ5TKglNf5LoHsQTpvQ==
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    

    Response Headers

    Access-Control-Allow-Credentials:true
    Access-Control-Allow-Origin:https://website.net
    Connection:close
    Content-Length:18
    Content-Type:text/plain; charset=utf-8
    Date:Fri, 21 Apr 2017 21:03:45 GMT
    Server:Apache/2.4.18 (Ubuntu)
    Vary:Origin
    X-Content-Type-Options:nosniff
    

    The server side is running on go at port 8888 behind an Apache reverse proxy. This is the Apache configuration:

    <VirtualHost *:443>
            ServerName website.com
    
            ProxyPreserveHost On
            ProxyRequests Off
            ProxyPass "/" "wss://localhost:8888/"
    

    mod_proxy and mod_proxy_wstunnel are installed.

    Is there something missing here? It seems like the request goes through but no connection is established.

    • gre_gor
      gre_gor about 7 years
      lcalhost? Is this a typo?
    • Andrei Savin
      Andrei Savin about 7 years
      yes, indeed, this was a typo, but it did not solve my problem
  • pimgeek
    pimgeek almost 5 years
    In my case it's a little bit tricky, Node-RED use /nodered/comms as EndPoint, so I have to change your rule to RewriteRule ^/nodered/comms wss://localhost:1880/nodered/comms [P,L] .
  • nix
    nix over 4 years
    You are missing parentheses around /nodered/comms. Without there won't be the matching group (a.k.a. back-reference) 1 available. See following link for details: httpd.apache.org/docs/2.4/rewrite/intro.html#regex
  • Oleg
    Oleg over 2 years
    Thanks, its works. Moreover, its works without port, just wss://domain.net