WebSockets and Apache proxy : how to configure mod

2019-01-04 17:29发布

I have :

  1. Apache (v2.4) on port 80 of my server for www.domain1.com, with mod_proxy and mod_proxy_wstunnel enabled

  2. node.js + socket.io on port 3001 of the same server.

Accessing www.domain2.com (with port 80) redirects to 2. thanks to the method described here. I have set this in the Apache configuration:

<VirtualHost *:80>
    ServerName www.domain2.com
    ProxyPass / http://localhost:3001/
    ProxyPassReverse / http://localhost:3001/
    ProxyPass / ws://localhost:3001/
    ProxyPassReverse / ws://localhost:3001/
</VirtualHost>

It works for everything, except the websocket part : ws://... are not transmitted like it should by the proxy.

When I access the page on www.domain2.com, I have:

Impossible to connect ws://www.domain2.com/socket.io/?EIO=3&transport=websocket&sid=n30rqg9AEqZIk5c9AABN.

Question: How to make Apache proxy the WebSockets as well?

12条回答
乱世女痞
2楼-- · 2019-01-04 18:15

Did the following for a spring application running static, rest and websocket content.

The Apache is used as Proxy and SSL Endpoint for the following URIs:

  • /app → static content
  • /api → REST API
  • /api/ws → websocket

Apache configuration

<VirtualHost *:80>
    ServerName xxx.xxx.xxx    

    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On

    <Proxy *>
         Require all granted
    </Proxy>

    RewriteEngine On

    # websocket 
    RewriteCond %{HTTP:Upgrade}         =websocket                      [NC]
    RewriteRule ^/api/ws/(.*)           ws://localhost:8080/api/ws/$1   [P,L]

    # rest
    ProxyPass /api http://localhost:8080/api
    ProxyPassReverse /api http://localhost:8080/api

    # static content    
    ProxyPass /app http://localhost:8080/app
    ProxyPassReverse /app http://localhost:8080/app 
</VirtualHost>

I use the same vHost config for the SSL configuration, no need to change anything proxy related.

Spring configuration

server.use-forward-headers: true
查看更多
放我归山
3楼-- · 2019-01-04 18:15

TODO:

  1. Have Apache 2.4 installed (doesn't work with 2.2), a2enmod proxy and a2enmod proxy_wstunnel.load

  2. Do this in the Apache config
    just add two line in your file where 8080 is your tomcat running port

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
    
查看更多
Root(大扎)
4楼-- · 2019-01-04 18:17

I finally managed to do it, thanks to this topic.

TODO:

1) Have Apache 2.4 installed (doesn't work with 2.2), and do:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Have nodejs running on port 3001

3) Do this in the Apache config

<VirtualHost *:80>
  ServerName www.domain2.com

  RewriteEngine On
  RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
  RewriteCond %{QUERY_STRING} transport=websocket    [NC]
  RewriteRule /(.*)           ws://localhost:3001/$1 [P,L]

  ProxyPass / http://localhost:3001/
  ProxyPassReverse / http://localhost:3001/
</VirtualHost>

Note: if you have more than one service on the same server that uses websockets, you might want to do this to separate them.

查看更多
你好瞎i
5楼-- · 2019-01-04 18:17

With help from these answers, I finally got reverse proxy for Node-RED running on a Raspberry Pi with Ubuntu Mate and Apache2 working, using this Apache2 site config:

<VirtualHost *:80>
    ServerName nodered.domain.com
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*)           ws://localhost:1880/$1 [P,L]
    RewriteCond %{HTTP:Upgrade} !=websocket [NC]
    RewriteRule /(.*)           http://localhost:1880/$1 [P,L]
</VirtualHost>

I also had to enable modules like this:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
查看更多
Bombasti
6楼-- · 2019-01-04 18:18

In addition to the main answer: if you have more than one service on the same server that uses websockets, you might want to do this to separate them, by using a custom path (*):

Node server:

var io = require('socket.io')({ path: '/ws_website1'}).listen(server);

Client HTML:

<script src="/ws_website1/socket.io.js"></script>
...
<script>
var socket = io('', { path: '/ws_website1' });
...

Apache config:

RewriteEngine On

RewriteRule ^/website1(.*)$ http://localhost:3001$1 [P,L]

RewriteCond %{REQUEST_URI}  ^/ws_website1 [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule ^(.*)$ ws://localhost:3001$1 [P,L]

RewriteCond %{REQUEST_URI}  ^/ws_website1 [NC]
RewriteRule ^(.*)$ http://localhost:3001$1 [P,L]

(*) Note: using the default RewriteCond %{REQUEST_URI} ^/socket.io would not be specific to a website, and websockets requests would be mixed up between different websites!

查看更多
在下西门庆
7楼-- · 2019-01-04 18:21

As of Socket.IO 1.0 (May 2014), all connections begin with an HTTP polling request (more info here). That means that in addition to forwarding WebSocket traffic, you need to forward any transport=polling HTTP requests.

The solution below should redirect all socket traffic correctly, without redirecting any other traffic.

  1. Enable the following Apache2 mods:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
    
  2. Use these settings in your *.conf file (e.g. /etc/apache2/sites-available/mysite.com.conf). I've included comments to explain each piece:

    <VirtualHost *:80>
        ServerName www.mydomain.com
    
        # Enable the rewrite engine
        # Requires: sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
        # In the rules/conds, [NC] means case-insensitve, [P] means proxy
        RewriteEngine On
    
        # socket.io 1.0+ starts all connections with an HTTP polling request
        RewriteCond %{QUERY_STRING} transport=polling       [NC]
        RewriteRule /(.*)           http://localhost:3001/$1 [P]
    
        # When socket.io wants to initiate a WebSocket connection, it sends an
        # "upgrade: websocket" request that should be transferred to ws://
        RewriteCond %{HTTP:Upgrade} websocket               [NC]
        RewriteRule /(.*)           ws://localhost:3001/$1  [P]
    
        # OPTIONAL: Route all HTTP traffic at /node to port 3001
        ProxyRequests Off
        ProxyPass           /node   http://localhost:3001
        ProxyPassReverse    /node   http://localhost:3001
    </VirtualHost>
    
  3. I've included an extra section for routing /node traffic that I find handy, see here for more info.

查看更多
登录 后发表回答