How to secure Nginx with SSL on Debian 9

In this article, we will show you how to obtain free SSL certificate and secure your web server with it. Also, we will show how to renew your certificates automatically.

- Debian 9
a sudo non-root user
- Nginx

We will use Let's Encrypt for our purpose. Let's Encrypt is a free, automated, and open certificate authority brought to you by the non-profit Internet Security Research Group.


Step 1. Install certbot (the client of Let's Encrypt)

Let's use following command:

sudo apt-get install python-certbot-nginx -t stretch-backports


Step 2: Obtain an SSL Certificate

To obtain an SSL certificate for your website we can use Webroot plugin:

The Webroot plugin works by placing a special file in the /.well-known directory within your document root. This file would be opened (through your web server) by the Let's Encrypt service for validation. You may need to explicitly allow access to the /.well-known directory.

Open your nginx configuration:

sudo nano /etc/nginx/sites-available/default

And add the following code to SSL block:

location ~ /.well-known {
allow all;

Also, we can change our root directory path. Let's change it to /var/www/html/reheda

Save and close it.

And we should create our new root directory inside the /var/www/html

sudo mkdir /var/www/html/reheda

Create simple index.html file inside root directory to be able to check it:

echo "<html><head><meta charset='UTF-8'><title>Document</title></head><body>Hello world</body></html>" | sudo tee --append /var/www/html/reheda/index.html

Check your Nginx config file for syntax errors with:

sudo nginx -t

If no errors are found, restart Nginx:

sudo systemctl restart nginx

Now that we know our webroot-path, we can use the Webroot plugin to request an SSL certificate with these commands. Here, we are also specifying our domain names with the -d option. If you want a single cert to work with multiple domain names (e.g. and, be sure to include all of them.

certbot certonly -a webroot --webroot-path=/var/www/html/reheda -d -d -d -d -d -d -d

After certbot initializes, you will be prompted to enter your email and agree to the Let's Encrypt terms of service. Afterwards, the challenge will run. If everything was successful, you should see something like this:

- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/ Your cert
will expire on 2017-09-05. To obtain a new or tweaked version of
this certificate in the future, simply run certbot again. To
non-interactively renew *all* of your certificates, run "certbot
- If you lose your account credentials, you can recover through
e-mails sent to
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt:
Donating to EFF:

After obtaining the cert, you will have the following PEM-encoded files:
- cert.pem: Domain's certificate
- chain.pem: The Let's Encrypt chain certificate
- fullchain.pem: cert.pem and chain.pem combined
- privkey.pem: Certificate's private key

The files themselves are placed in a subdirectory in /etc/letsencrypt/archive. However, Let's Encrypt creates symbolic links to the most recent certificate files in the /etc/letsencrypt/live/your_domain_name directory. Because the links will always point to the most recent certificate files, this is the path that you should use to refer to your certificate files.

Make sure that the received certificate is exactly what we need with the necessary domains:

cat /etc/letsencrypt/live/*/cert.pem | openssl x509 -text | grep -o 'DNS:[^,]*' | cut -f2 -d:

Generate Strong Diffie-Hellman Group

To further increase security, you should also generate a strong Diffie-Hellman group:

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


Step 3: Configure TLS/SSL on Nginx

We will make a few changes to our configuration:
-create a configuration snippet containing our SSL key and certificate file locations.
-create a configuration snippet containing strong SSL settings that can be used with any certificates in the future.
-adjust the Nginx server blocks to handle SSL requests and use the two snippets above.

This method of configuring will allow us to keep clean server blocks and reusable modules.

Create a Configuration Snippet Pointing to the SSL Key and Certificate

Let's create a new Nginx configuration snippet:

sudo nano /etc/nginx/snippets/

We should point to our certificates:

ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

Save and close the file.

Create a Configuration Snippet with Strong Encryption Settings

Next, we should create another snippet for SSL settings. This will help keep our server secure.

The parameters we will set could be reused in future Nginx configurations, so we will give the file a generic name:

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

We will add our preferred DNS resolver for upstream requests. We will use Google's for this guide. We will also go ahead and set the ssl_dhparam setting to point to the Diffie-Hellman file we generated earlier.

# from
# and
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver valid=300s;
resolver_timeout 5s;
# Disable preloading HSTS for now. You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

Save and close it.

Set the Nginx Configuration up to Use SSL

Let's back up our current server block file:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak

Open config file:

sudo nano /etc/nginx/sites-available/default

We will disable HTTP access and allow only HTTPS. Split your server block into two. We should listen to 80 (http) port and redirect to 443 (https) port.


# Default server server { listen 80 default_server; listen [::]:80 default_server; server_name; return 301$request_uri; }

server { # SSL configuration

listen ssl default_server; listen [::]:443 ssl default_server; include snippets/; include snippets/ssl-params.conf;

. . .

Save and close when finished.


Step 4: Enable the adjustment in the Nginx

Check nginx syntax:

sudo nginx -t

And restart if previous step is ok:

sudo systemctl restart nginx


Step 5: Set up SSL auto renewal

Let’s Encrypt certificates are valid for 90 days, but it’s recommended that you renew the certificates every 60 days to allow a margin of error.

To trigger the renewal process for all installed domains:

sudo certbot renew

Since the renewal first checks for the expiration date and only executes the renewal if the certificate is less than 30 days away from expiration, it is safe to create a cron job that runs every week or even every day:

sudo crontab -e

Add the following line:

17 */12 * * * /usr/bin/certbot renew --noninteractive --renew-hook "/bin/systemctl reload nginx" >> /var/log/le-renew.log

We should change 17 in the current line to random number between 0 and 59.

crontab -l | sed s/^17/$((RANDOM % 60))/ | crontab

So Certbot will try to renew certificates every 12 hours in the random minute of the hour.

Save and exit

The output produced by the command will be piped to a log file located at /var/log/le-renewal.log.



We will be happy to hear your thoughts

Leave a reply