How to Secure Nginx with Certbot on Ubuntu

Certbot is an easy-to-use client that fetches and deploys SSL/TLS certificates, thereby enabling encrypted HTTPS on your web server. Here’s how to secure your website using HTTPS.

Matthias Hagemann
6 min readMay 11, 2017

Certbot was developed by the Electronic Frontier Foundation EFF and others as a client for Let’s Encrypt. You can read more about Certbot or Let’s Encrypt, respectively.

Install Certbot ACME Client

To install the Certbot ACME client on Ubuntu 17.10 using the Nginx plugin, follow the official installation instructions:

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install python-certbot-nginx

Generate Strong Diffie-Hellman Group

You should also generate a strong Diffie-Hellman group to strengthen security. This command will generate a 2048-bit group:

$ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

It may take a few minutes until you have a strong DH group at located /etc/ssl/certs/dhparam.pem. We will be linking to this path later on, but for now, it is good that you have it ready.

Create a Configuration Snippet with Strong Encryption Settings

We will now create a global snippet that will define a few SSL settings to set Nginx up with a strong SSL cipher suite and enable advanced features that keeps our server secure. Let’s give that file a generic name (independent of any domain name):

$ sudo nano /etc/nginx/snippets/ssl-params.conf

In it, we want to use the recommendations offered by Cipherli.st. We can safely copy and paste the Nginx block in its entirety, except that we will replace $DNS-IP-1 $DNS-IP-2 with 8.8.8.8 8.8.4.4. If not, Nginx is going to return an error.

We will also set the ssl_dhparam setting (at the end) to point to the Diffie-Hellman file we generated earlier.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx >= 1.5.9
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

Save and close the file using the Write Out option, then Exit.

Adjust Nginx Server Blocks to Handle SSL Requests

To create a new server block on Nginx, open a new file on /etc/nginx/sites-available/. Replace example.com in below execution with your own domain name:

$ sudo nano /etc/nginx/sites-available/example.com

When I set up a new server block for a domain name, I always use the below template, so you can also just copy and paste below block into the newly created file:

server {  
server_name www.example.com example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
include snippets/ssl-params.conf;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
include snippets/ssl-params.conf;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

What it does is that it will redirect all incoming requests as follows:

  • Redirect all HTTP requests to HTTPS.
  • Redirect www.example.com to example.com (non-www).

It is really up to you as to how you want to redirect your domains. I simply prefer the non-www counterpart. It is generally good practice to allow only one option to view your site, otherwise browser caches or cookies are also going to be stored independent of each other (unless configured otherwise).

Enable your Server Blocks and Restart Nginx

Now that we have our server block file, we need to enable it. We can do this by creating symbolic links of these files to the sites-enabled directory, which Nginx reads from during startup.

We can create these links by typing:

$ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

This file is now in the enabled directory (while the original still lies in sites-available). It is a convenient way of Nginx to enable and disable sites by adding and removing links.

In order to prevent hash bucket memory problems that may arise from adding additional server names, we will adjust a single value within our /etc/nginx/nginx.conf file. Open the file:

$ sudo nano /etc/nginx/nginx.conf

Within the file, find the server_names_hash_bucket_size directive. Remove the # symbol to uncomment the line to make the content look like so:

http {
...

server_names_hash_bucket_size 64;

...
}

Save and close the file when done.

Test the configuration to make sure that there are no syntax errors in any of your Nginx files:

$ sudo nginx -t

You will get a result that looks like this if everything succeeded:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok  
nginx: configuration file /etc/nginx/nginx.conf test is successful

If no problems were found, restart Nginx to enable your changes:

$ sudo service nginx restart

Nginx should now be serving your domain name.

Obtain an SSL Certificate

You can now run Certbot with the Nginx plugin which will automatically configure everything for you.

$ sudo certbot certonly --nginx

A list of configured domain names for which you can install the respective certificates should appear.

Install the Nginx Plugin

If, instead, you get an obscure error message like The requested nginx plugin does not appear to be installed, then you may need to install the Certbox Nginx plugin first.

$ sudo apt-get install python-certbot-nginx

You should now be able to proceed with the installation:

$ sudo certbot certonly --nginx

Issue Certbot SSL Certificates and Chains

After the previous command, you will see a screen showing the list of enabled sites ready for certificate issuance:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: example.com
2: www.example.com
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel):

In our case, issue the certificates for both example.com and www.example.com because we have a server block for each non-www and www domain name. Therefore, enter 1,2 (the numbers preceding each listed domain name. Practically, we only need one, but I prefer to have valid certificates available just in case a visitor does end up on either the non-www or the www domain name somehow.

Be sure to have your domain name records (i.e. A-records for the non-www and www counterpart) configured and linked to your destination server beforehand, otherwise issuance will fail.

Link to the Newly Obtained SSL Certificate Files

Almost done. We just want to be sure that the newly generated SSL certificates are properly referenced in the server blocks.

Re-open the configuration file we edited earlier:

$ sudo nano /etc/nginx/sites-available/example.com

Add the references for ssl_certificate, ssl_certificate_key and ssl_trusted_certificate. They are all located in /etc/letsencrypt/live. Be sure to replace example.com with your domain name again.

server {  
...
}
server {
...
server_name www.example.com; ... ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem;
include snippets/ssl-params.conf;
...
}
server {
...
server_name example.com; ... ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
include snippets/ssl-params.conf;
...
}

Test Nginx Settings and Reload Nginx

First, we should check to make sure that there are no syntax errors in our files. We can do this by typing:

$ sudo nginx -t

We can safely restart Nginx to implement our changes:

$ sudo service nginx restart

Delete Certbot SSL Certificate

The generated certificate is very easy to delete with Certbot, if for any reason, it is no longer needed. I’ve written a separate tutorial for that.

Conclusion

Your web server is now using a free Let’s Encrypt TLS/SSL certificate to securely serve HTTPS content.

Note that these are short-term certificates which will expiry in 90 days. Run certbot renew every once in a while to renew those certificates that are due to expire in the next 30 days. You can also set up a cronjob to automate certificate renewals.

--

--

Responses (1)