How to configure Nginx with Let’s Encrypt on Debian/Ubuntu Linux

How do I secure my Nginx web server with Let’s Encrypt free ssl certificate on my Ubuntu Linux 14.04/16.04/18.04/20.04 LTS or Debian Linux 8.x/9.x/10.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. This page shows 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.

ADVERTISEMENTS

Say hello to acme.sh client to secure Nginx with Let’s Encrypt on Debian

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

Our sample setup to secure Nginx with Let’s Encrypt on Ubuntu

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

Step to configure and secure Nginx with Let’s Encrypt

The procedure is as follows to secure Nginx with Let’s Encrypt:

  1. Get acme.sh software to get SSL certificate:
    git clone https://github.com/Neilpang/acme.sh.git
  2. Create /.well-known/acme-challenge/ directory:
    mkdir -p /var/www/.well-known/acme-challenge/
  3. Obtaining an SSL certificate your domain:
    acme.sh --issue -w /DocumentRootPath/ -d your-domain
  4. Configure TLS/SSL on Nginx web server:
    vi /etc/nginx/sites-available/default
  5. Verify that auto-renewal cron job setup for your domain
  6. Open port 443 (HTTPS):
    ufw allow proto tcp from any to server-IP-here port 443

Install acme.sh

First, install the git and bc packages with apt-get command or apt command:
$ sudo apt-get install git bc wget curl socat
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'
OR
# acme.sh --install-cert -d theos.in \
--key-file /etc/nginx/ssl/theos.in/theos.in.key \
--fullchain-file /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

Where,

  1. --install-cert : Install the issued cert to nginx server
  2. -d theos.in : Specifies a domain, used to issue, renew or revoke etc
  3. --key-file /etc/nginx/ssl/theos.in/theos.in.key : After issue/renew, the key will be copied to this path
  4. --fullchain-file /etc/nginx/ssl/theos.in/theos.in.cer : After issue/renew, the fullchain cert will be copied to this path.
  5. --reloadcmd 'systemctl reload nginx' : After issue/renew, it’s used to reload the server
  6. Open tcp 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
    This entry is 1 of 13 in the Secure Web Server with Let's Encrypt Tutorial series. Keep reading the rest of the series:
    1. Set up Lets Encrypt on Debian/Ubuntu Linux
    2. Secure Lighttpd with Lets Encrypt certificate on Debian/Ubuntu
    3. Configure Nginx with Lets Encrypt certificate on Alpine Linux
    4. Nginx with Lets Encrypt on CentOS 7
    5. Apache with Lets Encrypt Certificates on RHEL 8
    6. CentOS 8 and Apache with Lets Encrypt Certificates
    7. Install Lets Encrypt certificates on CentOS 8 for Nginx
    8. Forcefully renew Let's Encrypt certificate
    9. OpenSUSE Linux and Nginx with Let's Encrypt Certificates
    10. Configure Nginx to use TLS 1.2 / 1.3 only
    11. Let's Encrypt wildcard certificate with acme.sh and Cloudflare DNS
    12. Nginx with Let's Encrypt on Ubuntu 18.04 with DNS Validation
    13. AWS Route 53 Let's Encrypt wildcard certificate with acme.sh

    🐧 If you liked this page, please support my work on Patreon or with a donation.
    🐧 Get the latest tutorials on SysAdmin, Linux/Unix, Open Source/DevOps topics:
    CategoryList of Unix and Linux commands
    File Managementcat
    FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
    Network Utilitiesdig host ip nmap
    OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
    Package Managerapk apt
    Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
    Searchinggrep whereis which
    User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
    WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04

    ADVERTISEMENTS
9 comments… add one
  • ling Mar 10, 2016 @ 7:27

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

  • Washington Mar 10, 2016 @ 17:54

    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.
  • pixel Mar 10, 2016 @ 18:06

    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 :)

  • Tom Mar 10, 2016 @ 19:01

    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/ ?

  • Istimsak Mar 14, 2016 @ 15:48

    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?

  • facio Apr 7, 2016 @ 10:31

    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.

  • swertz Jan 5, 2017 @ 0:34

    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.

  • RobertServer Jan 7, 2017 @ 13:38

    Hi,

    Can you add Lighttpd web server to your awesome script?

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre>, <code>...</code> and <kbd>...</kbd> for code samples.