Get Rid Of “locahost:#port#” With NGINX Reverse Proxies

I ran in to a situation recently where I needed to have one of my web projects running on port 80 on my Mac. Normally, when i start up this project in NodeJS, it runs on port 3000. But due to an interesting interaction between this project and another project, port numbers can’t be used in my project’s URL. Now you might think that this would be easy… just tell NodeJS to use port 80 and be done with it, right? In simple scenarios, that would work just fine. But I have anything but a simple scenario. My project has multiple web server instances (all communicating with a RabbitMQ – but that’s another story) and I need all of them to run on port 80, at different URLs… all on my Mac, where I am writing code and debugging the interactions. 

Right… so… multiple web server instances, all on port 80, with different DNS entries (URLs) for each. Great. There goes my day… or at least I thought so. Fortunately, NGINX to the rescue! Using reverse-proxies and a couple of entries in my /etc/hosts file, I was able to get all my servers up and running on their own port and have them appear at their own URL. 

The Gist Of A Reverse Proxy

The idea behind a proxy is that it does something on your behalf, and returns the result to you – sometimes modified to suit some specific purpose. A web proxy, for example, will request things from the web and filter them, or compress them further, or do other crazy things that web proxies might do. This is typically transparent to you, the end user. You make a request as you normally would, and the proxy picks it up on your behalf, as your request makes its way out to the internet and back.

A reverse proxy is one that happens in the opposite direction than the normal “handle this request and send me the results” type of proxy. Instead of your web browser request being proxied for some purpose, it’s the web server that is being proxied. In other words, a reverse proxy handles all the incoming requests for your web server, and sends the real request to the right place to handle it. It sits in front of the real services, pushes requests to the actual service and sends responses from the service back to the original requestor. 

In the case of wanting to stand up multiple services on port 80, or the need to get rid of a port # from a server and use port 80, a reverse proxy is the perfect solution. It won’t actually let you stand up multiple services on port 80, though. Instead, it will take a myriad of services that all run on different ports (3000, 3001, 4000, etc) and proxy them through port 80. Each service, running in any language that it wants to run in, will have it’s own port like you would expect. Nginx takes over port 80 and listens for specific requests. When it sees a request it knows how to handle, it takes that requests and proxies it to the actual service that can handle it. 

It’s a pretty slick setup, and one that should understand if you’re going to be doing scalable web development with multiple services on a single port or URL.

Installing And Starting NGINX

This was pretty easy for me on my Mac. I just used HomeBrew… didn’t even bother checking to see if it was possible. I just opened my terminal and ran

brew install nginx

and it worked! A few minutes later, I had a working version of nginx on my box. The basics of starting and stopping nginx are:

sudo nginx

This will start it up. Note that ‘sudo’ is only required if you’re trying to take a port below 1024. Anything above that doesn’t require sudo. To stop it, run

sudo nginx -s stop

This, as you expect, stops nginx (again, the sudo bit). But you might want to have nginx run on startup with your machine. That’s easy enough. Just add an nginx.plist file to your box and tell launchctl to run it at startup:

launchctl load -F /System/Library/LaunchDaemons/nginx.plist

Using this also prevents you from having to run ‘sudo’ to start it, since it’s a system command launching it. I recommend doing this so that you don’t have to launch nginx yourself again. The only downside is that it will take over your ports that you assign it to. If you need something else on port 80, you’ll have to stop nginx.

But then, the whole point of this configuration is to proxy many services from port 80 to the actual service – so why don’t you just do that instead?

Setting Up A Reverse Proxy

There are a cubic ton of options and configuration options in nginx. It’s a bit crazy, honestly, and took me a bit to figure out. The ones that you care about, though, include setting up a server with a location and various proxy_* settings in that location. A server is really a virtual server. It tells nginx what port to listen on, what server_name to look for (HOST header), and it allows different “locations” – virtual directories – to be pointed at different actual resources or processes. In the case of a reverse proxy, the location settings will contain the proxy_* configuration.

A very basic configuration for a reverse proxy might look like this (and this is the one I’m actually using right now)

server {
    listen 80;
    server_name dev.signalleaf.com;
    location / {
        proxy_pass http://localhost:3000/;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
    }
}

There are several things going on here. First off, I’m attaching nginx to port 80. Secondly, I’m telling it to listen for the server name of “dev.signalleaf.com” – this will be a HOST header that my browser sends. Then I’m telling it to watch for the root of that URL (the “/” location) and telling it how to handle the proxying of information from the original request over to my actual NodeJS/ExpressJS server that is running on localhost:3000

And A hosts Entry

Before I can make this work on my box, though, I need one more thing: an entry in my /etc/hosts file to make dev.signalleaf.com actually work on my box. This isn’t a real DNS entry that I want to have live on the internet. It’s just an entry that I want on my box, so that I can use dev.signalleaf.com as my development URL (getting rid of the :3000 port number in the process). 

127.0.0.1 dev.signalleaf.com

Let The Magic Happen

With nginx and my hosts file entry in place, I can hit http://dev.signalleaf.com on my box, and get the content that is typically served up from localhost:3000! My goal of getting rid of the port number has been achieved. From here, it won’t take a ton of extra configuration to get the additional services configured in nginx, all responding from port 80. 

 


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in nginx, Service, SignalLeaf, Web. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • iteamon

    You definately should look for something jak pow.cx for node

    • http://mutedsolutions.com Derick Bailey

      i’ve used pow.cx quite a bit. the limit that it has, it ruby/rack, though. nginx doesn’t care what web server / language / framework you’re using. you can proxy any server and every server you want at the same time… combine ruby, node, php, python and java all in one proxy if you want.

      • iteamon

        I have nginx + pow on my machine – I don’t need to care about ports at all and I still can make use of nginx. Point is, tool like pow solves 95% of use cases.

        • http://mutedsolutions.com Derick Bailey

          as long as your 95% is ruby/rack, sure. my 95% is nodejs

          • Typicode

            Well not anymore :)

            Just released a third party tool for Pow that lets you use it with Node projects (and other servers too).

            https://github.com/typicode/katon

            If you have the time to try it, I would love to know what you think about it.