Using acme.sh with nginx

2016-08-10 14:30

acme.sh is a script utility for the ACME spec used by Let's Encrypt. It is pretty simple and has no requirements, so I wanted to try using that in the server to issue and renew certificates rather than doing the process in my local machine and then copying the required files. It supports several modes for issuing the certificates, such as the Apache mode which I have used before, although there is no specific mode for nginx, so it is not possible to have completely automatic configuration if you use that server.

After installation we are now able to issue certificates, but there is a single problem: how should the challenge be solved? While it would be possible to stop nginx and have a local web server run on port 80, this might not be a good practice for production server. In my case, I cannot use another port (say, port 88) because that would require modifying some firewall rules, and that's kind of a bummer :)

So what are we going to do? Simple: use the webroot method and configure nginx to serve the challenges so that applications don't actually care about the challenge endpoint.

Edited November 5, 2017: Updated installation command to match updated commands and parameters in acme.sh.


First I'm going to define the webroot directory in the filesystem. For this example, I will use /var/www/le_root. Given that I installed acme.sh into the root user, let's also change the permissions so that nginx can access the directory.

mkdir -p /var/www/le_root/.well-known/acme-challenge
chown -R root:www-data /var/www/le_root

Now on to the nginx configuration itself. A simple location rule will do the job, but rather than copying that rule in all the virtual hosts, let's have a separate file in /etc/nginx/includes/letsencrypt-webroot and include it on demand:

# /etc/nginx/includes/letsencrypt-webroot

location /.well-known/acme-challenge/ {
    alias /var/www/le_root/.well-known/acme-challenge/;

Now in the virtual hosts (suppose /etc/nginx/sites-enabled/default):

# /etc/nginx/sites-enabled/default

server {
    listen 80;

    server_name mydomain.com;

    # ....

    # Let's Encrypt webroot
    include includes/letsencrypt-webroot;

Now requests to mydomain.com/.well-known/acme-challenge should be served by nginx. Make sure to reload nginx:

systemctl reload nginx.service

Issuing certificates

Once nginx is configured, we can issue the certificate:

acme.sh --issue -d mydomain.com -d www.mydomain.com -w /var/www/le_root

This creates the challenge files in the webroot (and removes them when done) and waits for verification of the challange. Once verified, we are presented with the location of the certificate, fullchain and key files.

Note that in the example I have created a certificate for both mydomain.com and www.mydomain.com.

Installing certificates

Installation of certificates with acme.sh will create a cron job that will automatically renew certificates and copy the relevant files to the locations you provide in the installation command. Furthermore, you can also specify the command to reload the server configuration.

Suppose we store the files in /etc/nginx/certs/mydomain.com directory (must be created beforehand), the command would be:

acme.sh --install-cert -d mydomain.com \
--cert-file /etc/nginx/certs/mydomain.com/cert \
--key-file /etc/nginx/certs/mydomain.com/key \
--fullchain-file /etc/nginx/certs/mydomain.com/fullchain \
--reloadcmd "systemctl reload nginx.service"

Now the files are copied and the cron job created. By default, certificates are renewed every 80 days.

The remaining step is to configure the virtual hosts to use SSL/TLS. A guide for this can be found in the official nginx documentation and remember to reload the server when done.