lets encrypt Nginx

Credit – Lets Encrypt

Encrypting the web traffic is a standard practice today. It is used by almost everyone regardless of whether you are a Bank making sure your customers feel safe to use online services, an e-commerce website trying to gain the confidence of your customers or just another blog writer who wants to look cool with the nice green lock in the address bar. In today’s world and with the recent developments/ enhancements in the TLS security (check the draft for v1.3 here https://tlswg.github.io/tls13-spec/) and especially with the launch of Let's Encrypt, there is only one reason to not encrypt your webtraffic.. Laziness! and No, encrypting all your traffic is not computationally heavy. So even if you are running your blog or any webservice on a $5/month 512MB droplet from digitalocean, you should be fine!

Anyways, so why did I make this move now? Call me stingy but I just cannot open my wallet to pay for services. However to my excitement, I got the news about a new certificate authority Let's Encryptwhich offers free SSL certificate! (well only 20 per domain per week but that’s, frankly, quite enough!). Anyways thats was it for me.. Now, I HAD to do it. I decided to use the Webroot plugin. There are many other methods but I find this to be quite straightforward.

What is Webroot?

Let’s Encrypt provides a number of ways to obtain SSL certificates and install it for different webservers such as certbot which is a client tool to fetch and deploy the SSL/ TLS certificates based on the webserver that you are using.
Webroot is actually a plugin for the certbot which allows you to obtain the SSL certificate. The way it works is by placing a special file .well-known in the webserver’s serving root location (like inside /usr/share/nginx/html/ or /var/www/html/. If you don’t remember it then check the /etc/nginx/sites-available/default configuration file and look for where document root points to).

Installing the certificate, however, is left upto the user.
I use Nginx for serving all my web requests so this post is specifically for nginx.
let’s start with the process of encrypting all your web traffic. I am assuming you have:

  • Root access to the server

  • Have A record updated in DNS settings (check with your domain name provider like godaddy, namespace etc) and it correctly points to your server’s ip address

  • Some familiarity with Nginx and its configuration files

  • Your favorite for a text editor (I use emacs.. so replace emacs -nw with your favorite text editor)

  • Maybe a coffe, if you fancy

Step1 : Installing Let’s Encrypt

Best way, as of now, to get the Let’s Encrypt client is to clone the master branch from their github repository in root’s home directory

~#> git clone https://github.com/letsencrypt/letsencrypt ~/

OR another way would be to get a slightly old version using apt-get package manager

~#> apt-get install letsencrypt

Step2: Nginx configuration for Webroot

Remember we talked about .well-known file that is used by Webroot plugin to validate the authenticity of your webserver? Lets put that in nginx configuration so that when Let’s Encrypts services try to run the check, they should be able to see the file.

~#> sudo emacs -nw /etc/nginx/sites-available/default

and inside the server block add another location:

location ~ /.well-known {
                allow all;
	}

Reload nginx

~#> systemctl reload nginx

Step3 : Obtaining SSL Certificate

Like I mentioned before, we will be using Webroot plugin to obtain SSL certificate

~#> cd letsencrypt
~#> ./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com

Replace example.com with your website name. certonly: only grab SSL certificate webroot: top-level root directory (“web root”) same as what is configured in nginx’s configuration file (/etc/nginx/sites-available/defalt) d: domain name

Answer the questions and in the end you should see a note which says something like:

IMPORTANT NOTES:
- If you lose your account credentials, you can recover through e-mails sent to your@email.com

- Congratulations! Your certificate and chain have been saved at
    /etc/letsencrypt/live/example.com/fullchain.pem. Your cert will expire on 2016-11-10. To obtain a new version of the certificate in the future, simply run Let's Encrypt again.

- Your account credentials have been saved in your Let's Encrypt 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 Let's Encrypt so making regular backups of this folder is ideal.

- If like Let's Encrypt, please consider supporting our work by:
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Make sure to note that the certificate that you received is only good for 90 days. After which you will have to run the script again. Hold on to that thought, we will talk more on that and write a script to automate this task for us :) Also note the location where the server’s pvt key and certificates are generated. We will need them in next step

Step4: Generate Diffie-Hellman Group

To strengthen the security even further, lets generate a nice 4096bit DH group which allows two devices to establish a shared secret over an unsecure communications channel.

~#> openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

This might take a moment

Step5: Nginx File Configurations:

Before we proceed, make a backup! (Not shown here)

~#> cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
~#> emacs -nw /etc/nginx/sites-available/default

Now, assuming you have default configuration, make a server block on top of the existing server block. It should look like this:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
    }

server {
       ... <your old stuff> 
       ...
       }

Make sure to replace example.com with your website domain name

Now in the second server block, add these lines:

server {
       # SSL configs
       listen 443 ssl http2 default_server;
       listen [::]:443 ssl http2 default_server;
       ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
       }

Make sure to replace example.com with your website domain name

Cipherli.st provides a nice “suggested settings” for stronger security. Grab the information for nginx and put that in the second server block.
Finally, your configuration should look something like this:

server {
        isten 80 default_server;
       	listen [::]:80 default_server;
        server_name example.com www.example.com;
	return 301 https://$server_name$request_uri;
       }

server {
        # SSL configs
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

	# Forcing better security
	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 <DNS-IP-1> <DNS-IP-2> valid=300s;
	resolver_timeout 5s;
	# Remove preloading!
	#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;

	# Adding DH group
	ssl_dhparam /etc/ssl/certs/dhparam.pem;
       }

Remember to replace and with your preferred choice Also remember to disable preloading unless you know what you are doing Add DH group in the end

Step 6: Almost done..

Restart nginx and check if it works

~#> systemctl restart nginx

Step7: Auto Renewing!

It would be a pain to first of all remember to renew the certificate every 90 days and then making sure that I do! Instead what we’ll do is set up a cronjob that will be triggered every Saturday at night 1am.

The command to renew the certificate is

~#> ./letsencrypt-auto renew

If you run this now, you should see a message saying No renewals were attempted since we just installed the certificate.

Let’s created a cronjob

~#> crontab -e

Following is the format for crontab

1. Entry: Minute when the process will be started [0-60]
2. Entry: Hour when the process will be started [0-23]
3. Entry: Day of the month when the process will be started [1-28/29/30/31]
4. Entry: Month of the year when the process will be started [1-12]
5. Entry: Weekday when the process will be started [0-6] [0 is Sunday]

So, if we want to schedule this task for every Saturday at 1am, the format should look like: 0 1 * * 6

the crontab entry would be:

0 1 * * 6 /root/letsencrypt/letsencrypt-auto renew >> /var/log/ssl-renew.log
5 1 * * 6 /bin/systemctl reload nginx

Always reload nginx service so that new process is started first and then the old one is terminated. Restarting the nginx kills the old process first so it might happen that there was some error and your nginx server doesn’t come back up!

That’s literally it! You now have an encrypted web traffic to and from your server in just 7 steps! Enjoy

Mohit Sharma
Mohit Sharma
Senior Infrastructure Engineer

DevOps Engineer with 10+ years of experience automating and scaling mission-critical systems. Proven expertise in Kubernetes, AWS, and Linux, with a strong focus on reducing operational complexity, enhancing developer experience, and fostering cross-functional collaboration. A track record of driving infrastructure automation, mentoring teams, and implementing best practices to deliver resilient, highly available systems.

comments powered by Disqus