In my Apache 2.4 vhost I'm trying to get all https:// traffic to carry on to port 443, but all ws:// traffic to forward onto ws://*:6969.


https://example.com/index would just go to https://example.com/index:443 as normal.

ws://example.com/anypathhere/ would be forwarded to ws://example.com/anypathhere:6969

So far I've tried the commented out values in the vhost.

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/pki/tls/certs/ca.crt
    SSLCertificateKeyFile /etc/pki/tls/private/ca.key
    DocumentRoot /var/www/html/

    RewriteOptions Inherit
    AccessFileName .htaccess
    AllowEncodedSlashes NoDecode

    DirectoryIndex disabled

    <Directory /var/www/html>
            Options +FollowSymlinks
            RewriteEngine On
            AllowOverride All
            Require all granted
            RewriteCond %{REQUEST_FILENAME} -d
            RewriteRule .* %{REQUEST_URI}index.php [L]

    ServerName localhost.localdomain

  #ProxyPreserveHost On
  #ProxyRequests Off
  #ProxyPassMatch / ws:// retry=0
  #ProxyPassReverse ws:// ws://
  #ProxyPass ws:// ws://
  #RewriteEngine On
  #RewriteRule ws:// ws://%{HTTP_HOST}/$1:6969 [NC,R=301,L]

All of these result in

Firefox can't establish a connection to the server at ws://

or a 400 error.


Unfortunately, I don't think there is an easy way to do this. Peter's answer doesn't work because Websockets piggybacks over HTTP(S) as an GET request with special headers. Whatever logic mod_rewrite uses to detect the URL scheme isn't smart enough to detect this, so the scheme always shows up as http or https.

In theory, you could look for the headers yourself, with something like the following syntax:

RewriteCond %{HTTP:Upgrade} ^websocket$ [nocase]
RewriteRule ^(.*)$ ws://$1 [proxy]

However, while this rule does match the opening client handshake (confirmed in my logs), this doesn't give me a working Websocket redirect. I suspect that mod_rewrite doesn't actually understand how to proxy Websocket connections, even if you can get the handshake to go to the right place.

That leaves us with JinnFox's answer. If you are able to move your Websockets endpoint to somewhere other than /, that might be an acceptable solution.

P.S. In the mean time, I have found that NGINX handles this case nearly seamlessly (scroll down to step 6 to find a code snippet that works for both HTTP and WS).


You truly need to add a vhost for ws traffic which is listening on *:80

Theres is one point, which fail either with RewriteCond or with ProxyPass at the wrong VHost-File. Your traffic is encrypted by the certificate you're using. Therefore the Connection won't be established correctly. Your remote host can't read it.

Based on the documentation in wikipedia:

The communications are done over TCP port number 80 [..]

Your additional VHost-File should look like this:

<VirtualHost *:80>
  ServerName localhost.localdomain
  ProxyPreserveHost On
  ProxyRequests Off
  ProxyPassReverse / ws://
  ProxyPass / ws://

As explanation. All your request going over http:80 are redirected to ws:6969.


The best way to do that would be to tell the client to use the right port... but if you want to proxy or redirect when they use the wrong port:

I don't know anything about ws://, but I believe you would need to use a RewriteCond for that, since a RewriteRule and similar don't contain the scheme to match. See the directive reference. This will probably only work if ws:// requests look like HTTP requets, or you have a module that specially handles ws:// requests. (To use RewriteCond, you can list it as many times as you want, and it applies only to the next RewriteRule [or similar?])

eg. for proxying:

RewriteEngine On
ProxyRequests Off
ProxyPreserveHost On

RewriteCond %{REQUEST_SCHEME} "^ws$"
RewriteRule "^(/?.*)$" ws://otherhost:6969/$1 [P]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.