How to configure Nginx with free Let’s Encrypt SSL certificate on Debian or Ubuntu Linux

Posted on in Categories , , , , last updated March 12, 2017

How do I secure my Nginx web server with Let’s Encrypt free ssl certificate on my Ubuntu Linux 14.04 LTS or Debian Linux 8.x server?

Let’s Encrypt is a free, automated, and open certificate authority for your website or any other projects. You can grab free TLS/SSL certificate including wildcard certificate to create encrypted HTTPS session for your site visitors. In this tutorial, I will explain how to use Let’s Encrypt to install a free SSL certificate for Nginx web server along with how to properly deploy Diffie-Hellman on your nginx server to get SSL labs A+ score.

Say hello to acme.sh client

acme.sh is a shell script client for LetsEncrypt free Certificate. It is very easy to use and works great with both Apache and Nginx. It works in the following mode:

  • Webroot mode (use for existing server)
  • Standalone mode (no nginx installed)
  • Apache mode
  • Dns mode

Please note that LetsEncrypt certificates are only valid for 90 days. Automatic renewal functionality is a bit tricky and need to be done via cron job. The official nginx installer is not yet functional.

Our sample setup

Fig.01: Our sample Nginx TLS/SSL Security with Let's Encrypt on Ubuntu Linux
Fig.01: Our sample Nginx TLS/SSL Security with Let’s Encrypt on Ubuntu Linux
  1. Default Nginx config file : /etc/nginx/sites-available/default
  2. Nginx SSL certification directory : /etc/nginx/ssl/theos.in/
  3. Nginx DocumentRoot (root) path : /var/www/html/
  4. Nginx TLS/SSL Port: 443
  5. Our sample domain: theos.in
  6. Dedicated public IP: 74.86.26.69

Install acme.sh

First, install the git and bc packages with apt-get command:
$ sudo apt-get install git bc wget curl
Sample outputs:

Fig.02: Install git and bc on Ubuntu/Debian Linux
Fig.02: Install git and bc on Ubuntu/Debian Linux

Clone repo

Next, clone the acme.sh client, enter:
$ cd /tmp/
$ git clone https://github.com/Neilpang/acme.sh.git

Sample outputs:

Cloning into 'acme.sh'...
remote: Counting objects: 1578, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 1578 (delta 3), reused 0 (delta 0), pack-reused 1563
Receiving objects: 100% (1578/1578), 503.02 KiB | 0 bytes/s, done.
Resolving deltas: 100% (645/645), done.
Checking connectivity... done.

To install acme.sh client to your system, enter:
$ cd acme.sh/
$ sudo -i
# ./acme.sh --install

Sample outputs:

[Fri Sep  2 13:08:52 UTC 2016] Installing to /root/.acme.sh
[Fri Sep  2 13:08:52 UTC 2016] Installed to /root/.acme.sh/acme.sh
[Fri Sep  2 13:08:52 UTC 2016] OK, Close and reopen your terminal to start using acme.sh
[Fri Sep  2 13:08:52 UTC 2016] Installing cron job
no crontab for root
no crontab for root
[Fri Sep  2 13:08:53 UTC 2016] Good, bash is installed, change the shebang to use bash as prefered.
[Fri Sep  2 13:08:53 UTC 2016] OK

Make sure the following line added to your ~/.bashrc file:

. "$HOME/.acme.sh/acme.sh.env"

After install, you must close current terminal and reopen again to make the alias take effect. Or simply type the following command:
$ sudo source ~/.bashrc
Test it (first become root user):
$ sudo -i
# acme.sh

All of the following command issued as a root user i.e. type the following command first:
$ sudo -i

Create /.well-known/acme-challenge/ directory

Type the following command (set D to actual DocumentRoot path as per your setup):
# D=/var/www/html
# mkdir -vp ${D}/.well-known/acme-challenge/
###---[ NOTE: Adjust permission as per your setup ]---###
# chown -R www-data:www-data ${D}/.well-known/acme-challenge/
# chmod -R 0555 ${D}/.well-known/acme-challenge/

Create directory to store SSL certicate

# mkdir -p /etc/nginx/ssl/theos.in/

Generate your dhparams.pem file

You need to use a strong Diffie-Hellman (DH) group, regardless of the server software you use. The simplest way of generating a new group is to use OpenSSL. Type the following command to create the dhparam.pem file:
# cd /etc/nginx/ssl/theos.in/
I suggest that you generate a 4096-bit group:
# openssl dhparam -out dhparams.pem 4096
OR use the following command to speed up dhparams generation:
# openssl dhparam -out dhparams.pem -dsaparam 4096
Sample outputs:

Generating DH parameters, 4096 bit long safe prime, generator 2
This is going to take a long time
...............................................................+.........+.................................................................................................................................................................................+.............+......................................................................................................................................
..
.............................................................................................................+...................................................................................+.........+.......................................................................................................................................................................++*++*

Issue a certificate for theos.in domain

The syntax is as follows
acme.sh --issue -w /DocumentRootPath/ -d example.com
acme.sh --issue -w /DocumentRootPath/ -d www.bar.com -d bar.com
acme.sh --issue -w /path/to/www/htmlRoot/ -d example.com -k 2048

Where,

  1. --issue : Issue a new certificate.
  2. -w /DocumentRootPath/ : Specifies the web root folder for web root mode.
  3. -d example.com : Specifies a domain, used to issue, renew or revoke etc. Can be used multiple times.
  4. -k 2048 : Specifies the domain key length.

To issue a certificate for theos.in and www.theos.in, enter:
# acme.sh --issue -w /var/www/html -d theos.in -d www.theos.in
For example, if you give “no” to “key-length”, it will use default length 2048. In this example set “key-length” to 4096
# acme.sh --issue -w /var/www/html -d theos.in -d www.theos.in -k 4096
Sample outputs:

Fig.03: Issue a certificate
Fig.03: Issue a certificate

Configure TLS/SSL on Nginx web Server

Edit nginx.conf or /etc/nginx/sites-available/default as follows:
# vi /etc/nginx/sites-available/default
Add the following configuration directives

## START: SSL/HTTPS theos.in ###
server {
    #------- Start SSL config with http2 support ----#
    listen 74.86.26.69:443 http2;
    server_name theos.in;
    ssl on;
    ssl_certificate /etc/nginx/ssl/theos.in/theos.in.cer;
    ssl_certificate_key /etc/nginx/ssl/theos.in/theos.in.key;
    ssl_session_timeout 30m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
    ssl_session_cache shared:SSL:10m;
    ssl_dhparam /etc/nginx/ssl/theos.in/dhparams.pem;
    ssl_prefer_server_ciphers on;
 
    ## Improves TTFB by using a smaller SSL buffer than the nginx default
    ssl_buffer_size 8k;
 
    ## Enables OCSP stapling
    ssl_stapling on;
    resolver 8.8.8.8;
    ssl_stapling_verify on;
 
    ## Send header to tell the browser to prefer https to http traffic
    add_header Strict-Transport-Security max-age=31536000;
 
    ## SSL logs ##
    access_log /var/log/nginx/theos.in/ssl_access.log;
    error_log /var/log/nginx/theos.in/ssl_error.log;
    #-------- END SSL config -------##
 
    # Add rest of your config below like document path and more ##
}
## END SSL theos.in ######

Save and close the file.

Install the issued certificate to Nginx web server

Type the following command:
# acme.sh --installcert -d theos.in --keypath /etc/nginx/ssl/theos.in/theos.in.key --fullchainpath /etc/nginx/ssl/theos.in/theos.in.cer --reloadcmd 'systemctl reload nginx'
Sample outputs:

[Fri Sep  2 15:19:56 UTC 2016] Installing key to:/etc/nginx/ssl/theos.in/theos.in.key
[Fri Sep  2 15:19:56 UTC 2016] Installing full chain to:/etc/nginx/ssl/theos.in/theos.in.cer
[Fri Sep  2 15:19:56 UTC 2016] Run Le_ReloadCmd: systemctl reload nginx
[Fri Sep  2 15:19:56 UTC 2016] Reload success

Open port 443

Type the following ufw command:
# ufw allow proto tcp from any to 74.86.26.69 port 443

Test it

Type the following url:
https://theos.in
Or visit SSL labs to test your TLS/SSL config:

Fig.03: SSL Labs A+ score
Fig.03: SSL Labs A+ score

How do I renew a certificate?

Type the following command:
# acme.sh --renew -d theos.in
Sample outputs:

[Fri Sep  2 15:23:16 UTC 2016] Renew: 'theos.in'
[Fri Sep  2 15:23:16 UTC 2016] Skip, Next renewal time is: Mon Nov 21 15:07:55 UTC 2016
[Fri Sep  2 15:23:16 UTC 2016] Add '--force' to force to renew.

How do I upgrade acme.sh client?

Type the following command to upgrade acme.sh client to the latest code from https://github.com/Neilpang/acme.sh
# acme.sh --upgrade
Sample outputs:

[Sat Dec 24 17:22:50 UTC 2016] Installing from online archive.
[Sat Dec 24 17:22:50 UTC 2016] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Sat Dec 24 17:22:55 UTC 2016] Extracting master.tar.gz
[Sat Dec 24 17:22:56 UTC 2016] Installing to /root/.acme.sh
[Sat Dec 24 17:22:56 UTC 2016] Installed to /root/.acme.sh/acme.sh
[Sat Dec 24 17:22:56 UTC 2016] Installing alias to '/root/.bashrc'
[Sat Dec 24 17:22:56 UTC 2016] OK, Close and reopen your terminal to start using acme.sh
[Sat Dec 24 17:22:56 UTC 2016] Good, bash is found, so change the shebang to use bash as preferred.
[Sat Dec 24 17:22:56 UTC 2016] OK
[Sat Dec 24 17:22:56 UTC 2016] Install success!
[Sat Dec 24 17:22:56 UTC 2016] Upgrade success!

A note about cron job

A cron job will try to do renewal a certificate for you too. This is installed by default as follows (no action required on your part):

0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

9 comment

  1. Another dream comes true.
    I was hoping such a tool for a long time, good for our wallets.
    Thanks (the procedure worked fine for me).

  2. Hi, I followed the steps in this guide till I got unable to load Private Key, after I run:

    le issue /usr/share/nginx/html themain.com www.thedomain.com,other.thedomain.com 4096
    Outputs:

    unable to load Private Key
    139662025688736:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: ANY PRIVATE KEY
    printf: usage: printf [-v var] format [arguments]
    unable to load Private Key
    139831152248480:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: ANY PRIVATE KEY
    printf: usage: printf [-v var] format [arguments]
    unable to load key file
    140545094272672:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:703:Expecting: ANY PRIVATE KEY
    Register account Error.
  3. I follow your blog since about a year, but I want to thank you today for this great howto. I didn’t even knew the existence of let’s encrypt, this is a great service and you write a great guide to use it. Thank you :)

  4. Thanks. It is working for me. But, why do I need to create /.well-known/acme-challenge/ directory in /home/mydomain/ ? What is the purpose of the /.well-known/acme-challenge/ ?

  5. Interesting howto article. If you don’t mind me asking, what inspired you to write this howto, what did you see in “lets encrypt” that made you share such valuable information?

  6. It’s better to do:
    chmod -R 0555 /var/www/html/.well-known/acme-challenge/
    instead of
    chmod -R 0444 /var/www/html/.well-known/acme-challenge/
    because chmod 0444 will fail domain validation.

  7. Would you consider adding configuration instructions for Apache? In particular, an Apache configuration supporting multiple sites/domains. I understand how to create certificates but I’m not sure how they’d be applied.

Leave a Comment