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.
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.