Websocket server (https) is not working, maybe it's a vhost error on Apache?

137 Views Asked by At

I'm trying to make both a regular node http server work and a WebSocket server. the HTTP server (as in node) is working, but the WebSocket isn't.

I'm not sure what I'm doing wrong, maybe someone can point me into the correct direction?

On the front-end, I'm doing socket = new WebSocket('wss://sub.example.com:8080') which gives the following error: handle-chat.js:11 WebSocket connection to 'wss://sub.example.com:8080/' failed:

I'm running on https, with letsencrypt. I'm running on a VPS, with Apache I'm editing the vhost config for the :80 port, I'm trying to run the ws(s?) server on port 8080, and the http server on port 8000. My vhosts file mostly looks like this:

sub.example.com.conf

<VirtualHost *:80>
    ServerName sub.example.com
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
    
    # ProxyPass for WebSocket server
    ProxyPass "/wss/" "wss://localhost:8080/"
    ProxyPassReverse "/wss/" "wss://localhost:8080/"
    
    # ProxyPass for HTTP server
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/
    
</VirtualHost>

See, I'm not sure if and when it has to be ws or wss (proxyPass "/ws/" or "/wss/" or on "ws://localhost" or "wss://localhost"), or if I even can do it through the non ssl conf.

This is what the sub.example.com-le-ssl.conf looks like

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName sub.example.com
    
    # ProxyPreserveHost On
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/
    
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined


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

Or maybe it has something to do with ports.conf, which looks like this

# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf

Listen 80
Listen 81
Listen 82

<IfModule ssl_module>
    Listen 443
</IfModule>

<IfModule mod_gnutls.c>
    Listen 443
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

I did enable the required moduels

  • proxy_module
  • proxy_http_module
  • proxy_wstunnel_module

And I'm also running proxy_fcgi_module, http2_module and even some more modules.

I think my back-end code is correct as well, it's basically just this:

import WebSocket, { WebSocketServer } from 'ws'

const wss = new WebSocketServer({
    port: 8080
})

wss.on('connection', function connection(ws, req) {
    ws.on('error', console.error)
    
    ws.on('message', function message(data) {
        console.log('received: %s', data)
    })
    
    ws.send('something')
})

Edit:

I am doing systemctl restart apache2 whenever I change the vhosts files I did notice that when I go to http://sub.example.com:8080/ws/ whilst running the node app, it returns a response saying: Upgrade Required So something is definitely working correctly, but I assume the wss part is not working, or something.

This was when the vhosts file looks like

<VirtualHost *:80>
    ServerName sub.example.com
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
    
    # ProxyPass for WebSocket server
    ProxyPass "/ws/" "ws://localhost:8080/"
    ProxyPassReverse "/ws/" "ws://localhost:8080/"
    
    # ProxyPass for HTTP server
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/
    
</VirtualHost>

I also forgot to mention, I am running UFW on which I did allow the port: 8080

Thanks for reading :)

1

There are 1 best solutions below

0
nathnolt On

Well, I fixed it, using someone else's question.

Websocket only work with ws:// but not with wss://

The fix is basically setting it up on the vhost side so it looks like the following:

Sub.example.com.conf now looks like this:

<VirtualHost *:80>
    ServerName sub.example.com
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    # ProxyPass for WebSocket server
    ProxyPass /ws/ ws://localhost:8080/ws/
    ProxyPassReverse /ws/ ws://localhost:8080/ws/
    
    # ProxyPass for HTTP server
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/
    
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
    
</VirtualHost>

sub.example.com-le-ssl.conf like this

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName sub.example.com
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    
    # ProxyPass for WebSocket server
    ProxyPass /ws/ ws://localhost:8080/ws/
    ProxyPassReverse /ws/ ws://localhost:8080/ws/
    
    # ProxyPass for HTTP server
    ProxyPass / http://localhost:8000/
    ProxyPassReverse / http://localhost:8000/


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

What I did on the front-end was connect with the 8080 server, but you shouldn't. So now the front-end code looks like this:

socket = new WebSocket('wss://sub.example.com/ws/')

socket.addEventListener('open', (event) => {
    socket.send('Hello Server!')
})

socket.addEventListener('message', (event) => {
    console.log('Message from server ', event.data)
})

The back-end code still looks the same.

Thanks :)