In my guide on setting up Nginx I included tweaks for caching SSL sessions and limiting the ciphers that are accepted, but I didn’t cover how I actually store and manage SSL certificates or go into how you create the pem files that Nginx uses.

By convention, I create a new directory to house all SSL certificates: /etc/nginx/ssl. I ramp down the permissions on it so that only root can access it, as Nginx starts off as root before spawning its individual worker processes:

sudo mkdir /etc/nginx/ssl
sudo chown -R root:root /etc/nginx/ssl
sudo chmod -R 600 /etc/nginx/ssl

Generating Your SSL Key and CSR

Ready to get a new SSL certificate? You’ll need to generate a private key and a CSR.

The Private Key

There are various recommendations on the length of keys you should use. In general I would never use a 1024 bit key anymore. With 2048 you should be good right now, but I’ve never found any practical reason not to go big… All my private keys are 4096 bits in length.

Note that some certificate authorities’ generation tools won’t support anything bigger than 4096, so don’t lose your head just yet.

So move into your new Nginx SSL directory and get started by generating your key: sudo openssl genrsa -out 4096

You should see your box churn for a while, and then you have a shiny new RSA private key named

The Certificate Signing Request

The next step is to create the signing request for your new certificate. This is what your certificate authority will parse to get the information they need to include on the certificate.

Different CAs use different pieces from this request. Some will only use it to get the private key they need to generate the certificate for, others will use all the organization information from it.

You have to tell OpenSSL which key to use in generating the CSR (the one you just created), and then answer a bunch of questions. Remember that the domain name you’re generating it for goes in the “Common Name” value.

If you’re generating a certificate for a root domain (ie:, not, etc.) don’t include the “www” subdomain. Most CAs will automatically add the www subdomain as an alternate name to the certificate for a root domain, but won’t necessarily realize that a request for should be changed to and have added… This is especially important if you’re adopting no-www.

sudo openssl req -new -key -out

After answering all the questions, is the file you need to upload to your CA when prompted.

The Final Step: Your Certificate Chain

After your CA has generated your certificate you should end up with a new crt file. For your own sanity down the line, make sure it’s named in the same pattern we’ve used for the other files: and upload it to your server and move it to your Nginx SSL directory.

Intermediate Certificate Authorities

Now for a little background on why we have to go through this hassle.

Every operating system (the one on your computer, the one on your server, the one on your phone) has a list of trusted “root” Certificate Authorities1. These are the big players that have spent a ton of money on supposedly securing their infrastructure (though it seems every week one of them has screwed up or been compromised lately): AOL Time Warner, Comodo, Equifax, Verisign, the US DoD…

In the old days you’d have to have gone to one of these companies directly and paid a large amount of money to get an SSL certificate. Thankfully (at least for my wallet), these days they sell what are called “Intermediate” certificates to other, smaller, companies.

Basically the intermediate certificate is like the one you get for your web server, saying that such-and-such company has done something to verify that you are who you say you are2 and they think you can be trusted. The intermediate certificate, however, also lets this new, smaller, company issue certificates to other people. Because they have been “certified” as “trusted” by the big name “root” CA, they are now authorized to certify you as owning…

So you end up with a “chain” of trust: The root CA trusts the Intermediate CA, who in turn trusts you.

The root CA doesn’t want to just hand out copies of its certificate, in case it turns out that the Intermediate CA gets hacked or wasn’t quite as trustworthy as they thought, so you have to include this chain of trust with your certificate any time you send it to a web browser so that it can track back up the chain of trust until it gets to a “root” CA that is in its known list of ones it should trust. If you don’t get this chain right you’ll get the dreaded “Untrusted certificate” warning in your web browser.

Creating the Chain

In Apache there are three configuration directives you usually include: SSLCertificateFile, indicating the file the certificate you got from your CA is in; SSLCertificateKeyFile, indicating the private key that you generated to go with it; and SSLCertificateChainFile, which includes all the certificates up the chain of trust to whichever root CA is trusted by browsers.

In Nginx, the process is simplified. Instead of specifying a different chain file, you simply include the chain of trust with your certificate.

Depending on which CA you have used, they may provide a single file that’s already chained together, or they may provide multiple certificates along the way. With StartSSL, who I use for all of my certificates, you’ll get two different ones you need to include: their intermediate certificate (either a class1 or a class2, depending on whether you’ve got a free certificate or a paid one) and the upstream CA that issued their certificate.

To create your chain file, you simply need to concatenate all of these certificates together in ascending order of trust — from your server certificate, all the way up to the root CA. You can do it all with one command once you’ve got all your certs ready:

cat ca.pem >

And out pops your unified chain file to feed into Nginx’s ssl_certificate directive when you’re configuring your vhost.

  1. These days your web browser probably maintains its own list, too. 
  2. Usually, just that you own the domain you’re requesting a certificate for. 
Originally published and updated .