45

The WebSocket protocol is an extension of the HTTP protocol. However, the proxy module of Apache2 does not seem to know about it, and throws away crucial headers, converting the call to a standard HTTP call.

Is there a way to make Apache2 either (1) understand WebSocket or (2) simply blindly pass on whatever it gets?

masegaloeh
  • 17,978
  • 9
  • 56
  • 104
Blixt
  • 595
  • 1
  • 4
  • 7

5 Answers5

24

There is now a module in the Apache trunk called mod_proxy_wstunnel which allows mod_proxy (ProxyPass/ProxyPassReverse) to pass through WebSocket traffic. Someone wrote a blog post about back-porting mod_proxy_wstunnel to Apache 2.4/2.2 and provided a patch to do so.

I figured out concrete instructions to set up mod_proxy_wstunnel on Ubuntu (tested with Ubuntu Server 11.10 and Apache 2.2.20) and posted them on my blog. I have copied them below:

# Check apache version (should be 2.2.20 as of writing, if not adjust the next step)
dpkg -s apache2

# Checkout apache source
svn checkout http://svn.apache.org/repos/asf/httpd/httpd/tags/2.2.20/ httpd-2.2.20

# Get patch and apply it
wget http://cafarelli.fr/gentoo/apache-2.2.24-wstunnel.patch
cd httpd-2.2.20
patch -p1 < ../apache-2.2.24-wstunnel.patch

# Build Apache 
svn co http://svn.apache.org/repos/asf/apr/apr/branches/1.4.x srclib/apr
svn co http://svn.apache.org/repos/asf/apr/apr-util/branches/1.3.x srclib/apr-util
./buildconf
./configure --enable-proxy=shared --enable-proxy_wstunnel=shared
make

# Copy the module and recompiled mod_proxy (for new symbols) to the ubuntu apache installation and update the permissions to match the other modules
sudo cp modules/proxy/.libs/mod_proxy{_wstunnel,}.so /usr/lib/apache2/modules/
sudo chmod 644 /usr/lib/apache2/modules/mod_proxy{_wstunnel,}.so
echo -e "# Depends: proxy\nLoadModule proxy_wstunnel_module /usr/lib/apache2/modules/mod_proxy_wstunnel.so" | sudo tee -a /etc/apache2/mods-available/proxy_wstunnel.load

# Enable the module (also make any configuration changes you need)
sudo a2enmod proxy_wstunnel
sudo service apache2 restart
Andrew Moss
  • 381
  • 3
  • 7
  • 2
    When I followed your guide, there was a step that you didn't have. After doing the apr checkouts I had to run `./buildconfig` to create the configure file. And there were a couple dependencies that it told me to install. – notbad.jpeg Aug 28 '13 at 16:47
  • does this hook up with Glassfish 4 over wss: (SSL) – Archimedes Trajano Jun 26 '14 at 01:56
  • 1
    @notbad.jpeg: You probably mean ./buildconf (not ./buildconfig) :-) – Erik Forsberg Jan 20 '15 at 08:30
  • 1
    Just my feedback... this installed and loaded in apache 2.2.22-1ubuntu1.10 from Ubuntu 12.04, but it didn't work for me in the end. The proxy was removing the "Upgrade" header (the source code says "RFC2616 13.5.1 says we should strip these headers"), which is a header the server expects, not just one hop, so it didn't work for me, and I have replaced it with an iptables DNAT rule instead. – Peter Feb 10 '16 at 09:58
11

There is nothing to indicate Apache httpd will support them anytime soon.

If you must run websockets through apache, try mod_pywebsocket. I have tried it, and it does work.

Here are a few alternatives I prefer:

h0tw1r3
  • 2,746
  • 18
  • 17
6

Looks like with a combination of the disconnect plugin and some extra code this is now possible:

http://blog.alex.org.uk/2012/02/16/using-apache-websocket-to-proxy-tcp-connection/

pablojim
  • 161
  • 1
  • 2
4

Please take a look at http://github.com/disconnect/apache-websocket

The apache-websocket module is an Apache 2.x server module that may be used to process requests using the WebSocket protocol by an Apache 2.x server.

  • I looked at the above github project. It does not act as proxy. quote `The module consists of a plugin architecture ...` – guettli Dec 18 '13 at 10:08
3

This add's to @Andrew Moss' answer as to how to correctly configure the VirtualHost to work with socket.io 1.0! Feel free to skip the part about CentOS!


If you're stuck on CentOS 6, here is how to do it:

  1. Download the backported source for the mod_proxy_wstunnel module here (either clone the Gist or download the files individually)
  2. Install everything necessary to build: yum install make gcc httpd-devel
  3. Setup an RPM Build environment (basically an unprivileged user and some directories)
  4. Copy the .c-file into the SOURCES subfolder of the environment and the .spec-file into the SPECS subfolder.
  5. Run rpmbuild -ba mod_proxy_wstunnel.spec
  6. The package is now in the SRPMS subfolder
  7. Install the package: rpm -i /path/to/package.rpm
  8. Profit

This will also automatically load the module in Apache, so you just have to restart it with service httpd restart.


Setting up a VirtualHost to actually serve the Socket.io server and client-script (which is by default available under http://your.server/socket.io/socket.io.js) is a bit more complicated on Apache 2.2, because of a Bug in the mod_proxy module:

Given the following rewrite rule:

RewriteRule    ^/ws(.*)$  ws://localhost:9000/ws  [P]

mod_rewrite treats this a filepath so the access log shows:

[26/Sep/2013:09:46:07 -0400] "GET /ws://localhost:9000/ws HTTP/1.1" 400 317

So, you can't use the ws-protocol in a rewrite-rule, because that will internally turn into an HTTP GET request.

There is a workaround though:

<VirtualHost *:80>
        ServerName your.server

        # Proxy socket.io Websocket
        RewriteEngine On

        # socket.io 1.0+ starts all connections with an HTTP polling request
        RewriteCond %{QUERY_STRING} transport=polling       [NC]
        RewriteRule /(.*)           http://localhost:8081/$1 [P]

        ProxyRequests Off

        # Explicitly send the request for the client-script to HTTP:
        ProxyPass /socket.io/socket.io.js http://localhost:8081/socket.io/socket.io.js
        ProxyPassReverse /socket.io/socket.io.js http://localhost:8081/socket.io/socket.io.js

        # Anything else goes to the WebSocket protocol:
        ProxyPass /socket.io/ ws://localhost:8081/socket.io/
        ProxyPassReverse /socket.io/ ws://localhost:8081/socket.io/

        # Any additional stuff (the actual site) comes here
        ProxyPass / http://localhost:8081/
        ProxyPassReverse / http://localhost:8081/
</VirtualHost>

This makes sure that everything sent to /socket.io goes to the ws://-protocol, except the request for long-polling (which is a fallback mechanism when WebSockets are not available) and the request for the client-library.

Lukas Knuth
  • 131
  • 3
  • Thanks @lukas-knuth .. this solved my issue proxing a nodejs app. A comment: I had to install the rpm into the RPMS directory .. instead of that on SRPMS – wnasich May 29 '22 at 13:28
  • 1
    A word of caution on proxying websockets through Apache: make sure you have your connection pools configured to handle all these long-standing connections. Otherwise, they get exhausted very quickly and the rest of your website won't be accessible, even though the utilization of NodeJS is very low. – Lukas Knuth Jun 03 '22 at 11:06