Monday, September 07, 2009

How to Proxy a Website to HTTPS with nginx, Start to Finish

Setting up a secure website is a trivial task often overlooked by site owners, possibly because they believe it is costly or technically challenging. Anyone who has set up their own HTTP webserver can easily proxy that site to HTTPS using free software. SSL certificates cost around £10 a year, a similar cost to registering a domain name.

I'm going to describe the steps I took to proxy QatarLiving to a secure site at QatarLiving is one of the most popular websites in Qatar, a small Middle Eastern state.

Step 0. Prerequisites

You'll need administrator (root) privileges to your own server to run an HTTPS site on port 443. I'm happy with my virtualized Linux box, hosted by Media Temple. It costs $50 a month, which is not the cheapest deal out there nor is the server particularly powerful, but I'm happy with the company's service and the box is sufficient to host all my websites and twitter feeds. If you sign up with Media Temple, you should install compilers and development tools.

Step 1. Buy your SSL certificate

Purchase an SSL certificate for your secure site. I bought mine from 123-reg. It is possible to run a secure site without buying a certificate, which is known as "self signing"; however, users of your site will be presented with scary warnings by their browser. It's best just to splash out on the SSL certificate: they're cheap.

Step 2. Downloads

If you've never come across nginx (pronounced Engine-X), don't feel too down-heartened. This tiny webserver fits into a niche of specialized webservers (including boa, lighttpd and thttpd) that compete on speed and small memory footprint. Many large sites have chosen nginx as their front-line server, including one of the very largest blogging sites, I chose to use nginx because its small footprint is crucial on my limited-memory server.

Download the latest development release of nginx, currently version 8.1.14 [homepage | nginx-0.8.14.tar.gz]

Step 3. Installation

Unpack the nginx source, compile and install. The following commands work for me, on my fairly standard centos Linux box, but some of the paths may require tweaking on other systems.

$ tar zxvf nginx-0.8.14.tar.gz
$ cd nginx-0.8.14
$ ./configure \
--prefix=/usr \
--sbin-path=/usr/sbin/nginx-ssl \
--conf-path=/etc/nginx-ssl/nginx.conf \
--http-log-path=/var/log/nginx_ssl_access_log \
--error-log-path=/var/log/nginx_ssl_error_log \
--pid-path=/var/run/ \
--http-client-body-temp-path=/var/tmp/nginx_ssl_client \
--http-proxy-temp-path=/var/tmp/nginx_ssl_proxy \
--http-fastcgi-temp-path=/var/tmp/nginx_ssl_fastcgi \
--with-http_ssl_module \
--with-http_stub_status_module \
--without-http_charset_module \
--without-http_gzip_module \
--without-http_userid_module \
--without-http_auth_basic_module \
--without-http_autoindex_module \
--without-http_geo_module \
--without-http_map_module \
--without-http_referer_module \
--without-http_fastcgi_module \
--without-http_limit_zone_module \
--without-http_browser_module \
$ make
$ su
# make install

If any errors appear with the above commands, it probably means you need to install some required library. In most cases, using Google on an error message will provide the fastest solutions. Options may vary slightly between versions. Always check ./configure --help for the current list.

Step 4. Server Configuration

Download your server SSL certificate and private key, rename them to server.crt and server.key, and move them to your nginx config directory (/etc/nginx-ssl/). If you've bought a cheap SSL certificate like I did, the certificating authority probably won't be built into your users' browsers by default. You'll need to find the certificate of your CA and concatinate it with your server's SSL certificate. Your server.crt will then look something like this:

[your certificate here]
[your CA's certificate here]

Now, all that's left is to edit your config file /etc/nginx-ssl/nginx.conf. You'll want to play around with some of these setting, but they provide a good starting point.

# the number of worker processes should be
# equal to the number of processor cores in your server
worker_processes 4;

# disable error logging by uncommenting the next line
# error_log /dev/null crit;

events {
# max connections - 2048 is reasonable
worker_connections 2048;

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

# TCP tuning
sendfile on;
tcp_nopush on;
tcp_nodelay off;

# keepalive is highly beneficial for SSL
keepalive_timeout 10;

# disable logging
access_log off;

server {
listen 443;

# change this to your own website hostname

# these SSL options have been chosen to maximize
# throughput on small servers
ssl on;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;

location / {
proxy_set_header X-Forwarded-For $remote_addr;

# don't proxy static content, redirect instead
location ~* \.(gif|jpg|jpeg|png|ico|js)$ {
rewrite ^/(.*?)$$1 permanent;

Start up your server by running nginx-ssl as root:

$ su
# /usr/sbin/nginx-ssl

Tail the error log (/var/log/nginx_ssl_error_log) if the server fails to start.

Further Reading

Nginx English documentation

Useful changes to default nginx configuration file

O3 Magazine - Open-Source SSL Acceleration

SSL optimization and security

HTTPS performance tuning

Installing an intermediate authority certificate under nginx

Why SSL?

Outstanding issues

Cookies that contain the original site's domain will not be stored by the user's browser. Most websites don't set a domain within the cookie, so this isn't usually a problem. However, if your site is affected, it can be fixed by filtering the headers from the insecure site and removing domains. I'll post a blog entry in a few days about this, as it affects most Drupal sites including QatarLiving.

Navigation within a proxied site will only be successful if most links do not include the original hostname.