Setting Up SSL on nginx for Performance and Security

June 22, 2013 · by Philip Tellis ·

At WebPerfDays Silicon Valley 2013 we ended up with an intense debate about the usefulness of SSL vs. the performance of SSL. The primary problem seemed to be that there was no simple, step-by-step guide on how to set up SSL correctly.

Now, I’m no expert on SSL performance, but I did have to learn how to set SSL up at some point in the past, and thought I’d share some of that here.

Step 1. Get a certificate

The biggest stumbling block to setting up SSL is that most people don’t know how to get an SSL certificate or feel like they can’t afford one. This was definitely our problem when we started up. As a bootstrapped business, we had an eye on every dollar we had to spend. That’s when we came across StartSSL.


StartSSL is an awesome service that gets you free or really cheap SSL certificates. Their policy is to only charge you for services where they need a human to do some work, so you pay if you need to verify that you own, or are a representative of your company, but you don’t pay if you only need to prove that you control the domain that you need a certificate for.

So, for Step 1, go to, sign up for an account, and get a certificate.

You should create your own Certificate Signing Request and just get StartSSL to sign that. It’s far more secure since your private key is never shared with a third party, not even the CA.

Creating a CSR

Use the following openssl command to create a Certificate Signing Request:

openssl req -out mycert.csr -new -newkey rsa:2048 -nodes -keyout mycert.key

And follow the instructions.

The process is a little long, but just follow every one of their instructions and you’ll be good.

One suggestion I do have is that you use Firefox to visit the StartSSL site. It just seemed to work better with Firefox than with Chrome or Safari.

Step 2. Download your signed certificate and the certificate chain from StartSSL

StartSSL has instructions for this, which will be included in an email they send to you. You should store all of these in a secure location.

You should have the following files:

  • ca.pem
  • mycert.crt

You should also transfer your mycert.key file (generated when you created the CSR above) to your server. I put all these files into /etc/nginx/ssl

Make sure mycert.key is owned by root and has 0600- permissions:

chown root *.pem mycert.crt mycert.key chmod 0600 mykey.key

Note that you can call your mycert.{key,crt} files anything you want. I typically use my_domain_name.{key,cert} to make it easier to identify.

Step 3. Create your certificate chain

Now, combine all the pem files into a single .pem file:

cat mycert.crt ca.pem > mycert.pem

This file is what you need.

Step 4. Add the certificates to your nginx configuration and configure nginx

This is the easy part. In your nginx configuration, inside your server block, add the following:

listen [::]:443 ssl default_server; ssl_certificate /etc/nginx/ssl/mycert.pem; ssl_certificate_key /etc/nginx/ssl/mycert.key; ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # default on newer versions ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:RC4-SHA:RC4-MD5:ECDHE-RSA-AES256-SHA:AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:AES128-SHA; ssl_prefer_server_ciphers on;

And put this outside your server block:

ssl_session_cache builtin:1000 shared:SSL:10m;

You definitely want TLSv1.2 enabled, and you want to prefer server ciphers for better security.

You should also make sure you have keepalive on:

keepalive_timeout 120; # 120 second keep alive

Step 5. Enable OCSP Stapling (or don’t)

I haven’t figured this out completely yet, but it’s related to the ssl_stapling on parameter.

Leaving this in here as a placeholder for someone to fill in. There seems to be some debate over whether this is good or not. On the one hand, it appears to be good for performance, but on the other hand, the specification appears to be so badly designed, that it will probably be insecure.

Step ☹. Do NOT enable SSL compression

While this is a good idea for performance, there have been vulnerabilities found in SSL when compression is enabled, so at least for any current implementations, do not enable SSL compression (it’s disabled by default).

This is unfortunate because it means that the cert chain will be sent uncompressed. This could grow up to about 7.5KB in our case (your case may be different). This doesn’t seem like a lot, but remember that this gets sent as part of the header, which means you could easily exhaust a single TCP window before any content is sent across. This would require an extra roundtrip to get the first byte of data across.

Step ☺. That’s it

That’s basically it. Start up nginx and you should be serving your site over SSL.

Things to note

You will need a separate IP address for each SSL server, or you’ll need SNI support. nginx supports SNI, but Internet Explorer on Windows XP and Android version 2.x do not support it, so they’ll get a big Insecure Content warning (it’s not pretty when that happens on a mobile phone).

When you’re done, you should run your site through the server test at SSL Labs:

Any conclusion?

Well, at this point at least, there’s no way we can move to SSL everything with the performance problems still in place, but I hope this blog post gets rid of the initial complaint that there’s no tutorial on how to set it up.


Thanks to the attendees at WebPerfDays for suggesting this, Steve Souders for constantly driving us to make things faster, Mike Damm for pointing us to tools, and Benjamin Black for reviewing and suggesting fixes to this document.