nginx configuration

Originally published Dec 3, 2014·Tagged #deployment, #nginx, #configuration

I use nginx over Apache for a number of reasons. First of all, its config files make a lot more sense to me. Since I work mostly with C-family languages, I'd rather have a nice-looking config file with curly braces rather than some fake knockoff XML. Plus, it's blazing fast compared to Apache.

Plus, if you're using Apache to proxy requests to an app running on another machine/port, Apache will remember that your app is down and automatically serve a "Bad Gateway" page for a while, even after you start the server again. This is pretty annoying for a development environment, since if you refresh the page but forgot to restart the server, Apache will cache the fact that the server is down, and you'll have to restart Apache to clear this cache. Nginx will also show the "Bad Gateway" page if the app is down, but will still try to proxy requests through as they come in, meaning that if you forgot to start your app server, you can just bring it up and retry the request and nginx will just work as expected.

99% of the time, when I use nginx, I'm using it to reverse-proxy some app I'm working on. It's much easier, more flexible, and more secure to run an application as a non-root user and then update my nginx config to point to it, than to try and run something on port 80. Doing this (mostly) decouples where my app's running from the address that users type to get to it, meaning I can move stuff around very fluidly.

Despite its simplicity, I still occasionally forget the syntax for specifying a virtual host. The term "virtual host" comes from Apache, and represents a particular machine on a particular port, referred to by a particular hostname. (Nginx calls them "server blocks", but the term "virtual host" is a little closer to how I think about them.) Suppose I have a server appserver.example.org running an app Foo on port 3000 and another app Bar on port 4000. I'd set up my domain names so that foo.example.org and bar.example.org point to appserver.example.org. Then, in nginx, I'd create two virtual hosts, one for Foo and one for Bar, that reverse proxy the apps' contents to the outside world.

nginx.conf would look like this:

worker_processes  1;

events {
  worker_connections  1024;
}

http {
  include       mime.types;
  default_type  application/octet-stream;

  sendfile        on;

  keepalive_timeout  65;

  # Block 1: foo.example.org
  server {
    listen 80;
    server_name foo.example.org;

    location / {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_pass http://localhost:3000;
    }
  }

  # Block 2: bar.example.org
  server {
    listen 80;
    server_name bar.example.org;

    location / {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_pass http://localhost:4000;
    }
  }

  # Block 3: catch-all
  server {
    listen 80 default_server;
    server_name _;

    root /usr/share/nginx/html;
  }
}

Blocks 1 and 2 ensure that requests to those hostnames are routed to our app servers, which are listening on their ports on localhost. Block 3 isn't really necessary, but a catch-all so that any requests that don't match one of the above hosts gets served static HTML from our /usr/share/nginx/html directory.