WebSockets and Apache proxy: how to configure mod_proxy_wstunnel?
node.jsApacheProxyWebsocketsocket.ionode.js Problem Overview
I have :
-
Apache 2.4 on port 80 of my server, with mod_proxy and mod_proxy_wstunnel enabled
-
Node.js +
socket.io
on port 3001 of the same server
Accessing example.com
(with port 80) redirects to 2. thanks to this method with the following Apache configuration:
<VirtualHost *:80>
ServerName example.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 example.com
, I have:
Impossible to connect ws://example.com/socket.io/?EIO=3&transport=websocket&sid=n30rqg9AEqZIk5c9AABN.
Question: How to make Apache proxy the WebSockets as well?
node.js Solutions
Solution 1 - node.js
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 example.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.
Solution 2 - node.js
Instead of filtering by URL, you can also filter by HTTP header. This configuration will work for any web applications that use websockets, also if they are not using socket.io:
<VirtualHost *:80>
ServerName www.domain2.com
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:3001/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:3001/$1 [P,L]
ProxyPassReverse / http://localhost:3001/
</VirtualHost>
Solution 3 - node.js
May be will be useful. Just all queries send via ws to node
<VirtualHost *:80>
ServerName www.domain2.com
<Location "/">
ProxyPass "ws://localhost:3001/"
</Location>
</VirtualHost>
Solution 4 - node.js
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.
-
Enable the following Apache2 mods:
sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
-
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>
-
I've included an extra section for routing
/node
traffic that I find handy, see here for more info.
Solution 5 - node.js
For me it works after adding only one line in httpd.conf as below (bold line).
<VirtualHost *:80>
ServerName: xxxxx
#ProxyPassReverse is not needed
<b>ProxyPass /log4j ws://localhost:4711/logs</b>
<VirtualHost *:80>
<VirtualHost *:80>
Apache version is 2.4.6 on CentOS.
Solution 6 - node.js
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
Solution 7 - node.js
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
Solution 8 - node.js
My setup:
- Apache 2.4.10 (running off Debian)
- Node.js (version 4.1.1) App running on port 3000 that accepts WebSockets at path
/api/ws
As mentioned above by @Basj, make sure a2enmod proxy and ws_tunnel are enabled.
This is a screenshot of the Apache config file that solved my problem:
The relevant part as text:
<VirtualHost *:80>
ServerName *******
ServerAlias *******
ProxyPass / http://localhost:3000/
ProxyPassReverse / http://localhost:3000/
<Location "/api/ws">
ProxyPass "ws://localhost:3000/api/ws"
</Location>
</VirtualHost>
Hope that helps.
Solution 9 - node.js
User this link for perfact solution for ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
You have to just do below step..
Go to /etc/apache2/mods-available
Step...1
Enable mode proxy_wstunnel.load
by using below command
$a2enmod proxy_wstunnel.load
Step...2
Go to /etc/apache2/sites-available
and add below line in your .conf file inside virtual host
ProxyPass "/ws2/" "ws://localhost:8080/"
ProxyPass "/wss2/" "wss://localhost:8080/"
Note : 8080 mean your that your tomcat running port because we want to connect ws
where our War file putted in tomcat and tomcat serve apache for ws
.
thank you
My Configuration
ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected
Solution 10 - node.js
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!
Solution 11 - node.js
For "polling" transport.
Apache side:
<VirtualHost *:80>
ServerName mysite.com
DocumentRoot /my/path
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPass /my-connect-3001 http://127.0.0.1:3001/socket.io
ProxyPassReverse /my-connect-3001 http://127.0.0.1:3001/socket.io
</VirtualHost>
Client side:
var my_socket = new io.Manager(null, {
host: 'mysite.com',
path: '/my-connect-3001'
transports: ['polling'],
}).socket('/');
Solution 12 - node.js
TODO:
-
Have Apache 2.4 installed (doesn't work with 2.2),
a2enmod proxy
anda2enmod proxy_wstunnel.load
-
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>