Wednesday, June 12, 2013

Setting up nginx, ssl, and virtual hosts

To install nginx you have a few options depending on the flavour of operating system you are running. You can either install it via a package manager, or download the source and compile it yourself. Using the package manager will mean you will get the latest known stable version for your OS, and have to worry less about working out dependencies or conflicts. However, you may be a version or two behind the official release.


installing nginx via yum/apt-get/rpm


If you’ve got a preferred package manager on your system then it should be as easy as one of the following (choose whichever is appropriate): sudo yum install nginx sudo apt-get install nginx


Alternatively you can download the rpm and install using that:


wget ftp://ftp.univie.ac.at/systems/linux/fedora/updates/9/i386.newkey/nginx-0.6.32-1.fc9.i386.rpm

rpm -Uvh nginx-0.6.32-1.fc9.i386.rpm

installing nginx via source


If none of the above is suitable, then you’re off to compile it on your own. The following should do the trick and include the libraries/packages we need:


wget http://sysoev.ru/nginx/nginx-0.7.2.tar.gz

tar zxvf nginx-0.7.2.tar.gz

cd nginx-0.7.2

./configure –prefix=/usr/local/nginx –sbin-path=/usr/local/sbin –with-debug –with-http_ssl_module

make

sudo make install

sudo nginx

configuring an nginx virtual host


Now that it’s up and running, time to configure nginx to know about a virtual host (or two) on your system. For now, lets just serve some static content so we get a feel for the config files:


sudo nano /usr/local/nginx/conf/nginx.conf

You’ll see the default nginx config file, in recent versions it’s fairly well commented to help you understand what is going on. Scroll down and find the definition that looks like this:


server {

listen 80;

server_name _;


#charset koi8-r;


#access_log logs/host.access.log main;


location / {

root /usr/share/nginx/html;

index index.html index.htm;

}


error_page 404 /404.html;

location = /404.html {

root /usr/share/nginx/html;

}

}

I wont go into too much detail here, the important thing to note is where the root location is. Here’s is specified as /usr/share/nginx/html, which means a request to http://yoursite.com/directory/file.html is going to try and serve the file on your server from /usr/share/nginx/html/directory/file.html. To create a new virtual host lets change this definition to look more like this:


server {

listen 80;

server_name www.yoursite.com;

root /var/www/yoursite.com/html;


location / {

index index.html index.htm;

}

}

Now all requests coming in to http://www.yoursite.com will be handled by this definition, but only requests for www.yoursite.com. If you want to handle another domain within this definition you can just put an additional server name in like this:


server_name www.yoursite.com www.othersite.com;

Alternatively, if you want to have www.othersite.com serve up content from a different location you would just add another server definition:


server {

listen 80;

server_name www.yoursite.com;

root /var/www/yoursite.com/html;


location / {

index index.html index.htm;

}

}


server {

listen 80;

server_name www.othersite.com;

root /var/www/othersite.com/html;


location / {

index index.html index.htm;

}

}

configuring nginx for your rails app


The hosts are setup, now we can have one of them start serving some rails apps. Lets change the virtual host definition we had above for www.yoursite.com to look like the following:


server {

listen 80;

server_name www.yoursite.com;

root /var/www/apps/yoursite/current/public;


# Set the max size for file uploads to 50Mb

client_max_body_size 50M;


# this rewrites all the requests to the maintenance.html

# page if it exists in the doc root. This is for capistrano’s

# disable web task

if (-f $document_root/system/maintenance.html) {

rewrite ^(.*)$ /system/maintenance.html last;

break;

}


location / {

# needed to forward user’s IP address to rails

proxy_set_header X-Real-IP $remote_addr;


# needed for HTTPS

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header Host $http_host;

proxy_redirect false;

proxy_max_temp_file_size 0;


# If the file exists as a static file serve it directly without

# running all the other rewrite tests on it

if (-f $request_filename) {

break;

}


# check for index.html for directory index

# if its there on the filesystem then rewite

# the url to add /index.html to the end of it

# and then break to send it to the next config rules.

if (-f $request_filename/index.html) {

rewrite (.*) $1/index.html break;

}


# this is the meat of the rails page caching config

# it adds .html to the end of the url and then checks

# the filesystem for that file. If it exists, then we

# rewite the url to have explicit .html on the end

# and then send it on its way to the next config rule.

# if there is no file on the fs then it sets all the

# necessary headers and proxies to our upstream mongrels

if (-f $request_filename.html) {

rewrite (.*) $1.html break;

}


if (!-f $request_filename) {

proxy_pass http://railsapp;

break;

}

}

}

What we have here is a series of rules that match the requested file against some regular expressions and underlying filesystem files. To tell nginx to serve any content that it finds matching the full requested path, starting in the directory defined as root we do the following:


if (-f $request_filename) {

break;

}

if (-f $request_filename/index.html) {

rewrite (.*) $1/index.html break;

}

if (-f $request_filename.html) {

rewrite (.*) $1.html break;

}

If that doesn’t work, we assume it need to be served by the rails app so we send it through using this:


if (!-f $request_filename) {

proxy_pass http://railsapp;

break;

}

But what does http://railsapp mean? No such server exists! Within nginx we need to define an upstream proxy. This will allow us to wrap all our mongrels up within a single definition, and nginx can take care of sending it through to the instances in the back end. To define the railsapp upstream mongrels we add the following outside our server definition:


upstream railsapp {

server 127.0.0.1:3000;

server 127.0.0.1:3001;

server 127.0.0.1:3002;

}

setting up your nginx ssl server


What if we want to serve out site over HTTPS for security reasons? No problem! We just add a server definition that listens on the correct port (that’s 443 for those of you playing along at home). So our definition would look like this:


server {

listen 443;

server_name www.yoursite.com;

root /var/www/apps/yoursite/current/public;


# Rest of config here…

redirecting all traffic to your ssl server


To be extra secure, we’ve decided that we want all traffic to be handled over SSL. Well we can use the ssl_requirement plugin in our rails app to completely prevent non-HTTPS access, just incase. We can also add this server definition to our nginx config to re-direct anybody who mistakenly links to the plain old HTTP address:


server {

listen 80;

server_name www.yoursite.com;

rewrite ^/(.*) https://yoursite.com/$1 permanent;

}

setting nginx error pages


Nginx will display some standard error messages should there be a problem, they’re about as attractive as the standard ones that come with rails though. If you want to jazz them up and make the whole experience less jarring for your users then update these lines:


error_page 404 500 502 503 504 /http_error.html;

location = /http_error.html {

root /var/www/errors;

}

And point it to the file you want to come up for all your errors. If you want to differentiate between various errors then configure each one individually:


error_page 500 502 503 504 /50x.html;

location = /50x.html {

root /var/www/errors;

}

error_page 404 /404.html;

location = /404.html {

root /var/www/errors;

}

directing nginx log output


If you’re running multiple virtual hosts, it will probably pay to give each a log file of it’s own rather than having a single jumbled mess to worry about. Basically any configuration you provide in the http definition you can re-define on a per-server basis within each server definition. So simply add these lines into the relevant virtual host/server definition:


server {

listen 80;

server_name www.yoursite.com;

root /var/www/apps/yoursite/current/public;


access_log /var/log/nginx/yoursite.access.log main;

error_log /var/log/nginx/yoursite.error.log debug;


# Rest of config…

}

make nginx serve cache content from a custom location


Some people may cache their rail output into a custom directory (and why wouldn’t you, it makes clearing the cache so much easier). If that’s the case, the usual nginx/rails configuration won’t work as expected. Your site will still work, you’ll just not be making the most of letting nginx serve your static content. To resolve it, add these lines in before the section that passes the request to your upstream proxy:


if (-f /cache$request_filename) {

rewrite (.*) /cache$1 break;

break;

}


if (-f /cache$request_filename.html) {

rewrite (.*) /cache$1.html break;

break;

}

Where /public/cache is where you are storing all of your cached content. Obviously, alter to fit accordingly.



Setting up nginx, ssl, and virtual hosts

No comments:

Post a Comment