nginx ssl https hardening ubuntu

Using Letsencrypt’s certificates and securing nginx’s SSL configuration

This tutorial will guide you through setting up an nginx webserver secured by a free certificate from letsencrypt.


This tutorial assumes you have an instance ready with Ubuntu 16.04 as OS. If you don't have one ready yet, you can follow the instructions on creating a cloud instance.

You'll also need to assign a floating IP to your instance.
Lastly, you'll need to make sure your desired domain name points to this floating IP.

Step 1: Updating the OS

From a security standpoint it is always advisable to install the latest update for your OS. This way, the latest security updates will be installed. For Ubuntu, you can use the following commands:

sudo apt update
sudo apt dist-upgrade

Step 2: Installing packages

Now we'll need to install nginx and letsencrypt. At the moment the latest version of nginx in the official Ubuntu repositories is version 1.10.0, so we'll use that version:

sudo apt install nginx letsencrypt

Step 3: Configuring nginx

The first part of this is creating the directory for your website. For example:

sudo mkdir -p /var/www/

Configuring nginx is pretty simple as the syntax is easy to understand. To start, remove the default configuration files. You can also choose to keep these files and use it as a default virtual host to keep a placeholder page.

sudo rm /etc/nginx/sites-enabled/default
sudo rm /etc/nginx/sites-available/default

We recommend keeping every site in its own configuration file, so now we'll create a new file. Try to keep the filename in line with your website's main domain name:

sudo editor /etc/nginx/sites-available/

Place the following configuration in your websites configuration file. Change all occurrences of with your domain name:

server {
        listen 80;
        listen [::]:80;
        root /var/www/;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    access_log /var/log/nginx/;
    error_log /var/log/nginx/;

Next, we need to enable the configuration. This is easily done with a symlink:

ln -s /etc/nginx/sites-enabled/ /etc/nginx/sites-available

Now we'll test the configuration for syntax errors and, if none are detected, reload nginx:

sudo nginx -t && sudo nginx -s reload

Now nginx is ready to serve normal HTTP requests. We'll need this for letsencrypt to work.

Step 4: Getting the certificate from letsencrypt

Letsencrypt's client, certbot, has an nginx installation plugin but according to the developers it is still considered experimental and buggy. We'll therefore choose for the manual installation method.
First, we'll need to request the certificate. With most CA's this means generating a private key and CSR and submitting the CSR through their website and paying them for the certificate.
Letsencrypt automates this entire process and also submits free certificates. To request a certificate for manual installation execute the following command (replace with your domain name).

Note, you can use multiple -d arguments to get multiple domain names in the same certificate.

letsencrypt certonly -a webroot --webroot-path=/var/www/ -d -d

The letsencrypt client will now ask you to enter your e-mail address and to accept the terms of usage.

The letsencrypt client will now generate a private key and CSR, request a certificate from the CA, validate that it has control of the domain and finally download the certificate and put it on your system.
The following files will now be placed on your instance:

  • /etc/letsencrypt/live/ - The signed certificate
  • /etc/letsencrypt/live/ - The certificate chain (root certificate and all intermediate certificates) needed to validate the certificate is signed by a trusted root certificate.
  • /etc/letsencrypt/live/ - A combination of both cert.pem and fullchain.pem.
  • /etc/letsencrypt/live/ - The private key. Keep this file private and secure at all times. Compromise of this file (for example, emailing this file, downloaded by a third party etc.) can allow third parties to hijack your SSL certificate.

Step 5: Setting up nginx to use the new certificate

An SSL Configuration can be as simple as this:

        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/;
        ssl_certificate_key /etc/letsencrypt/live/;

However, this simple configuration means a few weaknesses will be present. You can use Qualys's SSL Test to test your configuration and find the most common weaknesses.
At the time of this writing this simple SSL configuration will get you a "B" from this test. Of course, we want to do better than that. This test also still allows your website to be viewed unencrypted. Let’s fix this.

Step 6: Setting up HTTP to HTTPS redirect

This step is optional but does mean your website will be more secure and allow your users to automatically go to your SSL website.

First, open your nginx configuration file:

sudo editor /etc/nginx/sites-available/

Remove any SSL entries in your server configuration block and add the following settings:

        return         301 https://$server_name$request_uri;

This will redirect all normal HTTP traffic to the HTTPS counterpart. Next, we'll need to actually setup the HTTPS server. We'll also harden the SSL security configuration. Copy the following code into your configuration file (replace with your own domain):

server {
        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/;
        ssl_certificate_key /etc/letsencrypt/live/;
        ssl_protocols TLSv1.2;
    ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers On;
    ssl_session_cache shared:SSL:128m;
    add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
    ssl_stapling on;
    ssl_stapling_verify on;
    # Your favorite resolver may be used instead of the Google one below
    root /var/www/;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;


    location / {
            try_files $uri $uri/ =404;

    location ~ /\.ht {
            deny all;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

Please note that this configuration excludes some older OSs and browsers from connecting. Most notably IE 6 to 10 on Windows XP, Vista or Windows 7, Safari 5, 6 on Mac OSX 10.6 and 10.8 and Android until version 4.3.

To enable all these clients (with the only exception being IE6 on windows XP), we'll need to allow TLSv1. To do this, replace the ssl_protocols in the above config with this line:

ssl_protocols TLSv1 TLSv1.2;

This configuration will get you at least an A on SSL Labs.

Step 7: Header hardening

Besides the SSL configuration hardening, we can also add some HTTP Headers that further enhance the security of your website.
Below are some recommended headers. To read more about these headers and what options you can use and which are applicable to you, please visit

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Xss-Protection "1";
add_header Content-Security-Policy "default-src 'self'";

Step 8: Setting up automatic renewal

Since letsencrypt only issues certificates valid for 90 days, automatic renewal is important.
To do so, we'll create a renewal script, using letsencrypt's renew command. This command automatically renews all certificates with a validity of less than 30 days.

Save the following script in a logical location. We'll use /opt/

# This script renews all the Let's Encrypt certificates with a validity < 30 days
if ! letsencrypt renew > /var/log/letsencrypt/renew.log 2>&1 ; then
    echo Automated renewal failed:
    cat /var/log/letsencrypt/renew.log
    exit 1
nginx -t && nginx -s reload

Now we need to make sure it’s owned by root and executable by root:

chown root.root /opt/
chmod u+x /opt/

Next, we'll add it to cron for automatic execution:

sudo crontab -e

In our example we'll run it once a week, which is more than enough to renew the certificates before they expire:

@weekly /opt/

Was this article helpful?

Next article:

Using security groups to tighten Openstack security

One of the great features of OpenStack (and therefore our Fuga Cloud service) is the use of security groups. With security groups you’re able to very precisely define access rules to your platform and instances. In essence, they give you a free, fully customizable firewall. In this article we'll tell you how you can use customized security groups to grant access based on the roles of the instances within your Fuga platform.