danikaze
danikaze

Reputation: 1654

Apache reverse-proxy to NodeJS WebSocket Server

Before all, yes I have checked other posts like this, this or even this among others.

This being said, let me expose my case:

It works on the dev-env (localhost) completely fine (of course, it doesn't require any proxy)

I also use this curl command to confirm:

curl 'http://localhost:9021/ws' \
  --http1.1 \
  -H 'Pragma: no-cache' \
  -H 'Sec-WebSocket-Key: W/ZEACBv+gi6xA1JeMaO/A==' \
  -H 'Upgrade: websocket' \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: Upgrade' \
  -H 'Sec-WebSocket-Version: 13'

and it connects ok.

When I run it on the remote dev server, I have this Apache config:

<IfModule mod_ssl.c>
<VirtualHost *:443>
  ServerName dev.remote.com

  LogLevel warn
  ErrorLog "| /usr/bin/rotatelogs -l /home/user/log/dev.remote.com/error.%Y-%m-%d 86400"
  CustomLog "| /usr/bin/rotatelogs -l /home/user/log/dev.remote.com/access.%Y-%m-%d 86400" combined
  ServerSignature Off

  DocumentRoot /home/user/www/dev.remote.com

  <Directory /home/user/www/dev.remote.com>
    Options -Indexes +FollowSymLinks +MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  <Location />
    Options FollowSymLinks
  </Location>

  #
  # Serve static files directly with Apache
  #

  ProxyPass /public !
  Alias /public /home/user/www/dev.remote.com/public

  <Directory /home/user/www/dev.remote.com/public>
    Options -Indexes +FollowSymLinks +MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  ProxyPass /assets !
  Alias /assets /home/user/www/dev.remote.com/.next/static/assets

  <Directory /home/user/www/dev.remote.com/.next/static/assets>
    Options -Indexes +FollowSymLinks +MultiViews
    AllowOverride All
    Require all granted
  </Directory>

  #
  # Serve the rest with NodeJS/express
  #

  RewriteEngine On
  LogLevel alert rewrite:trace6

  RewriteCond %{HTTP:Connection} upgrade [NC]
  RewriteRule /ws  ws://localhost:9021/ws [P]

  ProxyPreserveHost On
  ProxyRequests off

  # WebSocket server (express) is on port 9021
  ProxyPass /ws  ws://localhost:9021/ws
  ProxyPassReverse /ws  ws://localhost:9021/ws

  # HTTP server (express) is on port 9020
  ProxyPass / http://localhost:9020/
  ProxyPassReverse / http://localhost:9020/

  <Proxy *>
    Require all granted
  </Proxy>


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

So 2 points here:

1. I'm sure the WebSocket server is working on the remote server by testing via ssh with the previous curl command. And it connects.

2. When I try opening the connection from the browser

ws = new WebSocket('wss://dev.remote.com/ws');

the mod_rewrite rule seems ok because the trace is like this:

[Fri May 28 17:37:16.272448 2021] [rewrite:trace2] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] init rewrite engine with requested uri /ws
[Fri May 28 17:37:16.272475 2021] [rewrite:trace3] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] applying pattern '/ws' to uri '/ws'
[Fri May 28 17:37:16.272505 2021] [rewrite:trace4] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] RewriteCond: input='Upgrade' pattern='upgrade' [NC] => matched
[Fri May 28 17:37:16.272513 2021] [rewrite:trace2] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] rewrite '/ws' -> 'ws://localhost:9021/ws'
[Fri May 28 17:37:16.272521 2021] [rewrite:trace2] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] forcing proxy-throughput with ws://localhost:9021/ws
[Fri May 28 17:37:16.272529 2021] [rewrite:trace1] [pid 15772] mod_rewrite.c(477): [client a.b.c.d:63015] a.b.c.d - - [dev.remote.com/sid#6916462d1a20][rid#69164d3530a0/initial] go-ahead with proxy request proxy:ws://localhost:9021/ws [OK]

So I deduct the rules are working and it's actually making the request via proxy to :ws://localhost:9021/ws

But... there's no connection received in the WebSocket server. Therefore, I get this on Chrome:

WebSocket connection to 'wss://dev.remote.com/ws' failed

Upvotes: 2

Views: 1857

Answers (1)

danikaze
danikaze

Reputation: 1654

The key was to have proxy_wstunnel mod active >_<

In case it helps anyone...

cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/proxy_wstunnel.load

stupid error, but I was too focused on the configuration, and it was about enabled mods

Upvotes: 3

Related Questions