Letsencrypt with acme.sh and Apache

I use the software acme.sh for getting certificates, a simple single shell script.

Features and benefits of this installation

This article describes a generic setup for Apache that has the following advantages:

  • The Apache configuration is never manipulated at runtime for fetching certificates.
  • Port 80 is only used for Letsencrypt.
  • All other web accesses are redirected from central to the respective https web pages.
  • We don’t need access to the DNS, as described here.

This is a great setup because it avoids complexity (no dynamic reconfiguration of components and no interface to DNS). This also makes it more secure.


In order for all this to work, we need the following:

  • A Linux system, for servers I like to use Debian.
  • An installed Apache webserver
  • Switch on mod_rewrite, on Debian with a2enmod rewrite.
  • Also of course mod_ssl for https, on debian with a2enmod ssl.
  • An installed version of acme.sh, I described the installation here. But we don’t use DNS-Challenge here.

Setup and configuration

First we create a directory where acme.sh can fight the so-called challenge with Letsencrypt:

Translated with www.DeepL.com/Translator

mkdir -p /var/www/letsencrypt
chown -R www-data:www-data /var/www/letsencrypt

The verification works in such a way that we use acme.sh to request a certificate from Letsencrypt and Letsencrypt then looks to see if it can retrieve a secret what we put in /var/www/letsencrypt. If so, it verifies that the domain belongs to us. For this we have to make this retrieval possible. We do this centrally for all web pages or virtual hosts on this web server.

Therefore we set up our own virtual central host in Apache, which intercepts all accesses to port 80, i.e. normal HTTP, and then does one of two things:

  1. if it is a Letsencrypt challenge, deliver the secret.
  2. if it is an access over port 80, forward to the requested web page, but with HTTPS, so a redirect.

We now create the file /etc/apache2/sites-available/000-default.conf or modify it if it already exists.

<VirtualHost *:80>
	# Put this in 000-default.conf
	Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
    <Directory "/var/www/letsencrypt/.well-known/acme-challenge/">
        Options None
        AllowOverride None
        ForceType text/plain
        RedirectMatch 404 "^(?!/\.well-known/acme-challenge/[\w-]{43}$)"
    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge [NC]
	RewriteCond %{HTTPS} off
	RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Please delete the part for port 80 from all other possibly configured virtual hosts, so that there are no more individual configurations that refer to something other than port 443. The whole thing also works for pure forwarding domains, which also have to be SSL-encrypted.

Now we switch on our configuration, check it for correctness and start or restart the Apache webserver if everything fits:

a2ensite 000-default.conf
apachectl configtest
systemctl restart apache2

Get the Certificates with acme.sh

Now everything is ready and we can retrieve the certificates for our respective domain for the first time, first as a test:

/etc/acme.sh/acme.sh --test --issue -d meine.domain.de -w /var/www/letsencrypt

and if it works:

/etc/acme.sh/acme.sh --issue -d meine.domain.de -w /var/www/letsencrypt

We now do this for every website we host on our web server.

Embedding the Certificates in the VHost Configurations

Now that we have the certificates, we can add or integrate them for our domains or VHosts as follows:

<VirtualHost *:443>
        ServerName meine.domain.de:443
        DocumentRoot /var/www/sites/support
        <Directory /var/www/sites/support>
                AllowOverride All
        ErrorLog /dev/null
        CustomLog /dev/null common

        SSLEngine on
        SSLCertificateFile /etc/acme.sh/certs/meine.domain.de/fullchain.cer
        SSLCertificateKeyFile /etc/acme.sh/certs/meine.domain.de/meine.domain.de.key

After that, don’t forget to get the Apache started:

systemctl restart apache2

Automation with cron

To make this all run automatically from now on, we now create an entry in cron by calling crontab -e, here every night at 0:17 o’clock:

17 0 * * * /etc/acme.sh/acme.sh --cron --home "/etc/acme.sh" --config-home "/etc/acme.sh/config" > /dev/null

The --home and --config-home parameters may differ depending on your setup of acme.sh.

That’s it. Have fun with it!