Nginx as reverse proxy for RabbitMQ MochiWeb server

Hi all,

I’ve suffered a hard day trying to find the solution to a problem that apparently has no a clear solution in the internet, at least, i couldn’t find it. So, i made mine.

RabbitMQ offers several plugins to enhance its usability, like the RabbitMQ management plugin (provides an HTTP-based API for management and monitoring of your RabbitMQ server, along with a browser-based UI and a command line tool) among others. Every plugin that requires any HTTP interaction works over a MochiWeb server automatically installed once installing the plugin. This server offers the 55672 port (default one) to interact with the API. For example, you can ask for the current RabbitMQ queues.

curl -u guest:guest http://localhost:55672/api/queues

In my case, i had to use Nginx web server as a reverse proxy for the MochiWeb server in order to have the management plugin web interface along with the default domain name, something like:

http://myserver/rabbitmq

Therefore, i configured Nginx in this super easy way:

location /rabbitmq/ { 
    proxy_pass http://0.0.0.0:55672/;
}

But the problems started when i tested the full web interface and there were some parts not working, like consulting the queues: go to the queues tab and click in a queue, you will get this:

Not found

The object you clicked on was not found; it may have been deleted on the server.

After some Nginx debugging i discovered that the management plugin was using non canonical URLs and Nginx was canonizing them, so the API didn’t work. For example, this API request:

http://myserver/rabbitmq/api/queues/%2F/my_queue

was being transformed by Nginx to:

http://myserver/rabbitmq/api/queues/my_queue

And the API threw 404.

The management plugin documentation explains how to configure a proxy server, but only for Apache:

AllowEncodedSlashes On
ProxyPass        /api http://localhost:55672/api nocanon
ProxyPass        /    http://localhost:55672/
ProxyPassReverse /    http://localhost:55672/

So i though, let’s do the same with Nginx!! And my surprise was that Nginx does not support the “nocanon” option.

I tried to find documentation in the internet and some people could more or less fix the problem using Perl scripts inside the Nginx configuration or the set_unescape_uri Nginx directive that lets you unescape HTTP GET parameters, but here there are seo-friendly URLs so this didn’t fit. Anyway, for both options, you had to recompile Nginx from scratch and that wasn’t an option for me because i can’t change our Nginx version in our infrastructure.

I spent some time testing different Nginx configurations, with new location directives, playing with rewrite and different regexps and i couldn’t manage to make it works. So i found out a final solution that is not as beautiful as i wanted, but at least, works very well.

location /rabbitmq/api/queues/ {
    proxy_pass http://0.0.0.0:55672/api/queues/%2F/;
}
location /rabbitmq/api/exchanges/ {
    proxy_pass http://0.0.0.0:55672/api/exchanges/%2F/;
}
location /rabbitmq/ {
    proxy_pass http://0.0.0.0:55672/;
}

I search for the places where that %2F parameter was being used and i created a Nginx location entry per one found making them redirect to the same place plus %2F.

I haven’t tested the full API, so maybe there are some other entries were that %2F is used and new Nginx locations need to be added.

If any of my readers have already faced this problem and know a better solution, please tell me!

And, i want to encourage Nginx developers to support the “nocanon” option like Apache does 😉

Advertisements

8 thoughts on “Nginx as reverse proxy for RabbitMQ MochiWeb server

  1. Thank you for the article, it helped me a lot. Although your exact configuration did not work in my case, the variation of it did the trick. Hope somebody finds it useful:

    location ~* /rabbitmq/api/(.*?)/(.*) {
    proxy_pass http://127.0.0.1:15672/api/$1/%2F/$2;
    }

    location ~* /rabbitmq/(.*) {
    rewrite ^/rabbitmq/(.*)$ /$1 break;
    proxy_pass http://127.0.0.1:15672;
    }

    Notice: for older versions of RabbitMQ 55672 should be used instead of 15672.

  2. I stuggled with this and the proposed solution with nginx 1.11.4 didn’t work fully as vhosts broke it and there were so many permutations (queues, exchanges, permissions) that it was unworkable. I found that the solution was very easy in the end. You only need one location and a bit of magic using the $request_uri parameter (the uri before nginx decodes it).

    location /rabbitmq/ {
    if ($request_uri ~* “/rabbitmq/(.*)”) {
    proxy_pass http://rabbitmq/$1;
    }
    }

    Hope this helps someone

    • I forgot to mention that I have an unstream in the config file that the proxy_pass is referring to. You could alternatively use proxy_pass http://localhost:15672/$1 and get the same result.

      upstream rabbitmq {
      server localhost:15672;
      keepalive 15;
      }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s