Configuring SSL in Nginx
This post aims to be a fairly thorough guide to getting SSL up and running on an Nginx server, largely for my own reference in future, but it may prove useful to anyone else who stumbles across it. I'm going to assume that you have a working installation of Nginx, or know how to get one going. If not, then bookmark this page and find a tutorial on how to get it going first, such as the section on Nginx in my post here, or the Installation and Beginner's Guide pages on the official site.
Get an SSL Certificate
The first step in setting up SSL on your server is to procure an SSL Certificate from an approved Certificate Authority (CA). The easiest way to do this is to purchase one from a certificate reseller such as CheapSSLs.com.
For a single site with no subdomains, such as a blog, that doesn't require all the extra features of more expensive certificates, then a certificate such as Comodo Positive SSL will do fine.
Once you've purchased the certificate, you'll need to create a Certificate Signing Request.
Creating a CSR
A Certificate Signing Request (CSR) is a file that contains the application information for your certificate, such as your location and the name of the domain you want to secure, as well as your Public Key. For creating the CSR we'll be using OpenSSL, so if you don't have that on your server you can install it with the following command:
> sudo apt-get install openssl
Before we start creating files, it's recommended that you have a proper location to store them in. On Linux systems this would be something like /etc/ssl/localcerts/
, so lets start there.
> sudo mkdir /etc/ssl/localcerts
> sudo cd /etc/ssl/localcerts
To generate the public and private keys that will be used for the CSR, use this command:
> sudo openssl req -nodes -newkey rsa:2048 -days 365 -keyout myserver.key -out server.csr
This will create two files; myserver.key
, the private key file, and server.csr
, the Certificate Signing Request. When you enter the above command, you should of course set the name of both files to one appropriate for the server you are securing.
Generally your CA should require you to use RSA 2048, this is specified with the -newkey rsa:2048
portion of the command. If you purchased a certificate that is valid for more than a year, then the -days 365
portion should be set to the number of days your certificate is valid.
Once you've entered the above command, you'll be asked to enter the details into your CSR. These details (with examples) are:
Country Name (2 letter code) [AU]: US State or Province Name (full name) [Some-State]: California Locality Name (eg, city) []: Los Angeles Organization Name (eg, company) [Internet Widgits Pty Ltd]: ACME Ltd Organizational Unit Name (eg, section) []: Widgets Common Name (eg, YOUR name) []: widgets.acme.com Email Address []:
And two 'extra', comletely optional details:
A challenge password []: An optional company name []:
If you enter a period '.' into one of the above fields then the field will be left blank.
One thing that seems to get glossed over is that the Common Name field is the fully qualified domain name (FQDN) of your site. So if you're wanting to secure example.com
, then that's what you put in the common name field. If you're securing a subdomain like subdomain.example.com
, then make sure you enter it like that. Unless you've bought a wildcard certificate, then your certificate will only cover a single domain or subdomain.
WARNING: Make very sure you've entered the Common Name correctly before you submit your CSR. (Yes, I have made this mistake. Don't do important things when you're tired.)
Once you've created the CSR, go to the site you purchased your certificate from and activate it. You'll be asked to enter your CSR into an online form, so open your CSR file up in a text editor and paste the entire contents into the enrollment form, including the BEGIN and END lines.
Installing your Certificate
Once your CSR has been approved, you'll get an email containing your new certificate, and the Root certificates required. Copy these to the folder where you intend to store your certificates. For this example we'll use the SSL localcerts directory we created previously /etc/ssl/localcerts/
.
You may want to combine the certificates together into one file. If so, then you can use the cat
command to concatenate the files in reverse order of authority. For example, if you've got a Comodo Positive SSL Certificate, then you should have three files; the certificate for your domain (eg. example.com.crt
), the intermediate certificate (eg. PositiveSSLCA2.crt
), and the root certificate (eg. AddTrustExternalCARoot.crt
). So to concatenate these files the command would be:
> sudo cat example.com.crt PositiveSSLCA2.crt AddTrustExternalCARoot.crt >> example.com.chained.crt
Configuring Nginx
You can now set about adding your certificate to your server configuration in Nginx. If you installed Nginx from a package, it should have SSL enabled already, otherwise if you're building from source you'll have to specifically enable it during configuration.
> ./configure --with-http_ssl_module --with_http_spdy_module
If you're using SSL then you may as well use SPDY to help speed up the connection.
Now you can configure your Virtual Hosts for SSL. In the server block of your configuration, you need to set it to listen on port 443, specify the certificate and private key, and SSL protocols and cipher settings.
This is an example of a basic Nginx Virtual Host file with SSL enabled:
server { listen 443 ssl spdy; server_name example.com; root /srv/www/example.com; index index.html; ssl_certificate /etc/ssl/localcerts/example.com.chained.crt; ssl_certificate_key /etc/ssl/localcerts/example.com.key; }
The listen directive specifies that the server should listen on the HTTPS port, 443, instead of the usual port 80, that SSL is on, and that it is using the SPDY protocol. The ssl_certificate directive points to the location of the chained certificate we created. The ssl_certificate_key directive points to the private key that was created when we created the certificate signing request.
The above provides the minimum configuration required to enable SSL on Nginx. All you need to do is restart Nginx, and you should be able to visit your site using HTTPS.
SSL Security in Nginx
Of course, the above basic configuration only uses the most basic default security, so you'll probably want to do some additional configuration.
The first thing you'll want to do is to set the SSL Ciphers your server will use. This post has a good explanation on the background of using ciphers. The exact list of ciphers you should use is dependent on how secure you want to be, and what performance considerations you have. The ciphers I use are:
ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;
This gives me an 'A' rating on the SSL Server Test, while still providing decent performance.
The rest of my SSL configuration is as follows:
ssl_session_timeout 5m; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ecdh_curve secp521r1;
ssl_session_timeout 5m; is the time it takes for the client's ssl session information to be removed from the cache. A longer time means that the ssl session won't have to be renegotiated as often, improving performance if there are a lot of connections. 5 minutes is the default time for many client browsers.
ssl_prefer_server_ciphers on; means that the server will prefer to use ciphers specified in the ssl_ciphers
directive over the ciphers preferred by clients.
ssl_session_cache shared:SSL:10m; lets Nginx use its own cache instead of the one provided by OpenSSL, thus allowing Nginx to separate SSL jobs between its own workers.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; is the list of supported protocols, which in this case is only TLSv1 or greater. SSLv3 and below are not recommended due to security vulnerabilities, and most browsers support TLS now.
ssl_ecdh_curve secp521r1; is the Elliptic curve key used for the ECDHE cipher.
If you want a much more in-depth look at securely configuring Nginx, then calomel.org has an excellently thorough post detailing nearly everything you need to know.
Redirecting to HTTPS
If you want to redirect all your traffic to the SSL version of your site, then you'll need to specify another Virtual Host to accept HTTP traffic and redirect it to HTTPS.
server { listen 80; server_name example.com; return 301 https://example.com$request_uri; }
This server block listens on port 80, and then performs an HTTP 301 redirect to the requested URI. As this is a permanent redirect of a full URL then return
rather than rewrite
is the correct Nginx directive to use.
Conclusion
You should now have a secure implementation of SSL on your website. You may want to use tools such as pagespeed to test the speed of your site, as it may require a bit of tweaking to get the best balance between performance and security.