This was written in around August 2015 and published in November of the same year. Updates will be documented below. An attempt will be made to keep this guide current. However, please test and research in order to verify it.


Communicating securely over the internet is a concern as of late. HTTP over SSL (or simply called HTTPS), is a way to facilitate such secure communications. Many discussions exist surrounding the reasons for running HTTPS, so this guide won’t go into those details. Briefly though, Google offers one very compelling reason, that is the ability of a website to be served over HTTPS will influence search results.

Preamble to the Guide

The goal of this guide is to show how to set up Nginx to serve pages in HTTPS. To determine at which point that has been accomplished, Qualys SSL Labs’s SSL Server Test will be used. The result of the test is a rating between A+ and F. This guide is how to get an A+ on that test.

An in–depth guide explanation of every detail about every piece of software and/or technology used herein is not a part of this writing. That information and more is readily available from the internet.

Prerequisites

This guide assumes that we have an existing web server powered by Nginx on some flavour of Linux. Also, be sure Nginx was built with SSL support (--with-http_ssl_module). There are many certificate providers out there. Most cost money, and the higher level of assurance required, the more money. For a free low–assurance certificate, check out StartSSL (this site uses a StartSSL–signed certificate). For this guide, we’ll create a certificate and have it signed by a commercial provider, such as StartSSL or one of the many other providers out there.

Create a Certificate

Choose or make a directory to store the certificate and its related files in. A sub–folder of Nginx’s directory will work fine.

mkdir /opt/nginx/ssl
cd /opt/nginx/ssl

Now we’ll create a certificate signing request, or CSR (read about a CSR here). The openssl tool will do this.

openssl req -new -days 365 -nodes -keyout example.ca.key -out example.ca.csr

Replace example.ca with the desired name of the key. For simplicities sake, the domain name is a good option. Check out man openssl for detailed information about all of the available options. For this example, req, says we want a CSR; -new, says this is a new request; -days, says to make this certificate valid for 365 days; -nodes, says to not encrypt the output key; -keyout, specifies the output file for the key; and -out, specifies the output file for the CSR.

When the command is run, it will prompt for some basic information as shown below. The ‘extra’ attributes are optional (simply press enter to skip them).

Generating a 2048 bit RSA private key
writing new private key to 'example.ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CA
State or Province Name (full name) [Some-State]:British Columbia
Locality Name (eg, city) []:Victoria
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Inc
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:example.ca
Email Address []:webmaster@example.ca

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Depending on your certificate provider, the steps to obtain the actual certificate will vary. Regardless of the provider, however, the file /opt/nginx/ssl/example.ca.csr will need to be given to the provider in order for them to generate the certificate. Save the certificate received from whichever provider chosen as /opt/nginx/ssl/example.ca.crt.

While on the certificate provider’s website, get their intermediate certificate. For example with StartSSL, copy the file sub.class1.server.ca.pem from here and save it as /opt/nginx/ssl/sub.class1.server.ca.pem.

Combine the Certificates

This step is quite simple. The two certificates just need to be placed in the same file in order to ‘combine’ them.

cat example.ca.crt sub.class1.server.ca.pem > example.ca_unified.crt

Replace sub.class1.server.ca.pem with the name of the saved intermediate certificate from your chosen provider.

Some providers may return a pre–combined certificate already. In that case, there’s no need for the above steps.

Diffie–Hellman Key Exchange

Without getting too much into cryptography, Diffie–Hellman—or DH for short—key exchange is a method of exchanging secrets without giving an eavesdropper the information to read those secrets. For a little more in–depth explanation, check out this video on Khan Academy. DH key exchange makes use of prime numbers (really big ones), and an issue exists where many servers are using similar numbers (read about it here). To counter this potential threat, let’s make sure the numbers generated are unique.

Make sure to be in the same directory as the other files created so far. Next, generate some unique DH key exchange parameters using openssl.

cd /opt/nginx/ssl
openssl dhparam -out dhparams.pem 2048

That’s it. The file, /opt/nginx/ssl/dhparams.pem, was created and contains what’s required for more secure DH key exchange.

Configure Nginx

Now it’s time for our main purpose, to configure Nginx to serve websites in HTTPS. Only the relevant lines of code are included in this configuration. Compare it with your configuration and add what’s needed.

server {
  listen 80;
  listen 443 ssl;

  server_name example.ca, www.example.ca;

  if ($scheme = http) {
    return 301 https://$server_name$request_uri;
  }

  ssl on;
  ssl_certificate /opt/nginx/ssl/example.ca_unified.crt;
  ssl_certificate_key /opt/nginx/ssl/example.ca.key;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
  ssl_dhparam /opt/nginx/ssl/dhparams.pem;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
}

The official documentation has details on what each configuration line does. One bit to notice is the if ($scheme = http) block. This will ensure that all requests are served under https://example.ca (that is, no www subdomain, and no http protocol support—it will redirect the user if they try).

Test

Restart Nginx and navigate to the website to make sure everything is working so far. To verify it all, we’ll use Qualys SSL Labs’s SSL Server Test. Enter the domain name. The test will take some time to perform. If everything worked out, the rating should be an A+.

Conclusion

Securing a website with HTTPS isn’t a simple task. This guide was just meant to touch on the basics in order to get a secure website up and running in a short amount of time. Consider reading more about the components used in order to gain a better understanding of what’s actually going on under–the–hood.

Resources

Here’re some curated links to resources on website security. More may be added over time.