nginx + SSL reverse proxy tutorial

If you have an existing HTTP application that you want to enable SSL for, nginx provides a convenient reverse proxy mode. In this mode, nginx is just responsible for providing SSL encryption; the actual HTTP content is still being served from your existing web server or load balancer.

First, you need to install nginx. On Ubuntu 11.10, you can install it directly from the Ubuntu repositories.

sudo apt-get install nginx

On older Ubuntu distributions, you will need to add a custom PPA.

sudo -s
nginx=stable # use nginx=development for latest development version
echo "deb http://ppa.launchpad.net/nginx/$nginx/ubuntu lucid main" > /etc/apt/sources.list.d/nginx-$nginx-lucid.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C
apt-get update
apt-get install nginx

Next, you need to generate a Certificate Signing Request and a secret key file. I created mine directly inside a new directory that we will point nginx to.

sudo mkdir /usr/local/nginx
sudo mkdir /usr/local/nginx/conf
cd /usr/local/nginx/conf
sudo openssl genrsa -des3 -out server.key 2048
sudo openssl req -new -key server.key -out server.csr

You will be asked a bunch of questions about the SSL certificate. This is semi-official information, so you want it to be accurate. The certificate authority may actually call/email people to verify some of the information. Technically, the only important field is the Common Name, which is the domain name you want the certificate for. You should look up some SSL creation tutorials to make sure you have the correct values.

In my case, I wanted a wildcard certificate, which can be used for all subdomains on the domain in question, so I entered *.example.com for the common name.

Now you get out your credit card. You need to purchase a certificate based on this .csr file. I chose Digicert. A wildcard certificate cost about $1300 for three years.

They will ask you for the csr file. You can also paste in the text of the file, which looks something like this.

-----BEGIN CERTIFICATE REQUEST-----
MIIC3zCCAccCAQAwgZkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNQTEPMA0GA1UE
BxMGQm9zdG9uMRcwFQYDVQQKEw5CdWxsaG9ybiwgSW5jLjEOMAwGA1UECxMFUmVh
Y2gxHDAaBgNVBAMUEyouYnVsbGhvcm5yZWFjaC5jb20xJTAjBgkqhkiG9w0BCQEW
FnJlYWNoYmV0YUBidWxsaG9ybi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCqayVplOo/PJhoufXSaFD7q66pmIfveWW3dlRNxltgk7l2jhusnsVz
37P9F/qwxUjn6ijgZnopXNMQn2UEPKr8J65DgdSyLEFYFIO1DtLEYxS4myL2Bjc+
mDEPirC05jDLOMMP7tr+607jWxOaM9p5G+kNjUc+gFCN4T7BSYKn1gnpmUPJDdYO
FOD0sZ4G0SWrEDWNhzp5fNAqtviApDWfFWcDL2FJIiYKethp3moLsu5eAycji3PE
hLqDZPXO3AQ+eaFAmRbkjB0imlbYgLn4MHWQWZHuPjvTV1puJfbnb4auMmu1xBCE
wKgTS7ZgHp/9RbR19W5aDNBkiM+WkdMhAgMBAAGgADANBgkqhkiG9w0BAQUFAAOC
AQEAkfuZ5GU7f1sxECH3hP2TOVRCvPay4R6J3Ql2782DaGjmqix8KsJMXNymVE8B
9+09NFKo9Mv+Rwb/bZCjwviEADLXeMCjm503UMgWjyH+ONY4bNOs6k6hZYM7rmVb
WnRgk2FXi9Kxgp5YMDQrTV1o2o6eykObqAfnWlG8Awka7w9aPNnp+HiFNtygB78R
oFP/8v4KGVbSOGsvXRNPGs7y6dYCBnPxd5OxoXlc06j7gnuPbHKh69Bo7e2gTRYp
T2g6GC3fgglMmq7pku775adR30E3CnS/Huc+MmBWC/TYYZv1Uv+8+sLnBZUDHgrH
oeMl/QXd6Nnev+IVx0am4moU0Q==
-----END CERTIFICATE REQUEST-----

The certificate authority vendor will produce a .pem file, which has both your new certificate and the vendor's certificate bundled together. Once you get the certificate via email, you can proceed to setup a reverse proxy in nginx. First, remove the passphrase from the .key file. Otherwise, nginx will prompt for it interactively every time the service starts.

sudo cp server.key server.key.org
sudo openssl rsa -in server.key.org -out server.key

Then you have to configure nginx to use the .pem file from the certificate authority, as well as your secret .key file. There is an excellent wiki for other configurations that nginx supports.

server {

    listen 443 default_server;
    server_name www.example.com;

    ssl on;
    ssl_certificate /usr/local/nginx/conf/server.pem;
    ssl_certificate_key /usr/local/nginx/conf/server.key;
    ssl_session_cache shared:SSL:10m;

    location / {

        proxy_pass http://localhost:8000; # my existing apache instance
        proxy_set_header Host $host;

        # re-write redirects to http as to https, example: /home
        proxy_redirect http:// https://;
    }
}

Note the proxy_redirect directive. I noticed that when my application performed a redirect internally, it was using the global URL, ie http://www.example.com. To keep the user on the SSL version w/o having to make my application SSL aware, or change to protocol relative redirects, I simply told nginx to re-write the redirect on the fly. Since I am only asking nginx to handle SSL traffic, it can safely use https for all redirects, given that I'm not redirecting to other sites.

That's it. You just need to start the server with "service nginx start". You can test that it's working in a browser, or "openssl s_client -connect localhost:443" for a more verbose test.



I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter