To obtain a Let’s Encrypt certificate, you have to prove that you control the domain name(s) the certificate will cover. The simplest and most common way to do this involves placing a special file at a special URL on your website, which Let’s Encrypt then checks by making an HTTP request to your server on port 80. Most popular ACME clients such as Certbot can easily automate this domain validation method.
Unfortunately, this doesn’t work in the case where port 80 is closed.
Open port 80 if you can
If you’re reading this because you voluntarily closed port 80 on your own server, please read this and this, and seriously consider just opening the port. In most cases it will be the simplest option.
It’s not an option for everyone, however - mostly because some people run home servers behind ISP-managed routers that don’t allow them to open port 80. If you’re in that boat, read on for some alternatives.
There are many alternatives to Certbot, which can be more capable in some ways and less so in others. One such alternative is acme.sh, which provides more options than Certbot for obtaining a certificate, but gives you a little less help with installing the certificate once you get it. In particular, it has good support for the two alternative methods (or “challenges”) for proving your control of your domain: TLS-ALPN-01, which uses port 443, and DNS-01, which involves creating a special DNS record and doesn’t require any open ports on your server at all.
You can get acme.sh by following the installation instructions on its GitHub page. It’s simplest if you do this as root, because it sets up a shell alias and cron job for the user you install it as. Remember to log out and log in again after installing it.
If you already have acme.sh installed
If you have an older version of acme.sh installed, it might not support the TLS-ALPN-01 challenge, or might not support your DNS service yet. You can upgrade it to the latest version by typing:
Set your email address
It’s optional but highly recommended to provide a valid email address so that Let’s Encrypt can contact you about any important updates (such as recently when the old TLS-SNI-01 challenge was being removed). You’ll also get reminders at this email address if anything goes wrong with your automated renewals and your certificates get close to expiring.
To set your email address using acme.sh (you can run this command at any time, but I’d advise doing it immediately so that you don’t forget):
acme.sh --update-account --accountemail firstname.lastname@example.org
Set the default server
Currently (late January 2021) acme.sh uses Let's Encrypt by default, but it also supports other certificate authorities. The developer recently announced an upcoming change to the default. If you want to continue using Let's Encrypt after the change, you can set the default explicitly:
acme.sh --set-default-ca --server letsencrypt
See the announcement for more details.
Get a certificate
If you can’t use port 80, the available alternatives are TLS-ALPN-01 (if you can use port 443) or DNS-01 (if you can create DNS records automatically).
Unfortunately, most web servers aren’t yet flexible enough to easily allow you to respond to TLS-ALPN-01 validation challenges while the web server is running, so you’ll have to stop your web server temporarily during the validation and start it up again afterwards.
You want to run a single command that will stop your web server, obtain a certificate, and then start up your web server again. This is important; if you can do everything in one command, acme.sh will reuse the same command parameters for automated renewals, which will make the renewal process very simple for you.
Here’s an example, assuming that your web server is Apache and you start and stop it using the
systemctl command from systemd:
acme.sh --issue --alpn --pre-hook 'systemctl stop apache2' --post-hook 'systemctl start apache2' -d example.com -d www.example.com
If you use a different web server or prefer to control it a different way, substitute the appropriate stop and start commands instead of
systemctl stop apache2 and
systemctl start apache2 above.
If your DNS service provides an API to allow automated updates, there’s a good chance that acme.sh can obtain a certificate by using that API to complete the DNS-01 validation challenge.
Unfortunately, almost every DNS service that provides an API, provides a different API. So there isn’t one set of instructions that works for every service. Instead, you’ll have to find your DNS service on this page and follow the relevant instructions there to issue a certificate.
Install the certificate
Here’s where acme.sh is a little different from Certbot; while Certbot tries to obtain and install the certificate in a single command, acme.sh does it in two separate steps. Also, acme.sh’s installer won’t attempt to automatically configure your web server for you; it’ll just copy the certificates to the correct location and optionally reload the web server.
You’ll have to decide where you want to keep your certificates and keys. Personally I like to create an
/etc/acme.sh/ directory and keep everything there, but you can put them wherever you want - just make sure the directory is appropriately protected:
mkdir /etc/acme.sh chown root:root /etc/acme.sh chmod og-rwx /etc/acme.sh
Now you can install the certificate. If you used the TLS-ALPN-01 challenge, use a command like the following:
acme.sh --install-cert -d example.com --key-file /etc/acme.sh/example.com-key.pem --fullchain-file /etc/acme.sh/example.com-cert.pem
If your Apache version is 2.4.7 or older, it will expect the certificate and intermediate (or “chain”) in separate files, rather than the single-file format that the above command generates and which is compatible with newer Apache versions, and Nginx. If you have one of these older Apache versions, replace:
--cert-file /etc/acme.sh/example.com-cert.pem --ca-file /etc/acme.sh/example.com-chain.pem
If you used the DNS-01 challenge, you’ll also need to tell acme.sh to reload your web server after installing the certificate (since it won’t have been stopped and started as part of the validation process). Here’s an example, assuming you’re using Apache and reloading it using the
systemctl command from systemd:
acme.sh --install-cert -d example.com --key-file /etc/acme.sh/example.com-key.pem --fullchain-file /etc/acme.sh/example.com-cert.pem --reloadcmd 'systemctl reload apache2'
If you use a different web server or prefer to control it a different way, substitute the appropriate command instead of
systemctl reload apache2 above. See also the note above about Apache versions.
In either case, after you’ve done this once, it will happen automatically when this certificate is renewed in the future.
Configuring your web server
You’ll also have to configure your web server to actually use this certificate. If you’re replacing an existing certificate from Certbot, this is easy: just edit the configuration file for the domain and change the relevant lines to point to the acme.sh certificates instead of the Certbot ones. For example, with Apache on Ubuntu, the configuration files are in
/etc/apache2/sites-enabled/ and have names ending in
-le-ssl.conf, and you’ll want to find the lines referencing files in
/etc/letsencrypt/live/ and change them to point to the corresponding files (which you just created above) in
/etc/acme.sh/ instead. Then reload the web server (e.g.
systemctl reload apache2).
If it’s a completely new certificate, you’ll have to configure the web server to serve HTTPS using the new certificate - unlike Certbot, acme.sh doesn’t try to do this for you. If you don’t know how to do this, Mozilla’s configuration generator can help, or you can read the documentation for Apache or Nginx.
When you installed acme.sh it should have automatically created a cron job to perform automated renewals. So you shouldn’t need to do anything else to set this up.
In case of trouble
If you run into problems following this procedure, please post a question on the Let’s Encrypt Community Forum. Feel free to mention me (@jmorahan).
There are a few other options that I’m aware of, but I haven’t personally tested all of them.
The Caddy web server has built-in Let’s Encrypt support and can automatically obtain certificates for the websites it serves. It can do this using the HTTP-01, TLS-ALPN-01 or DNS-01 challenges, so it will work just fine even with port 80 closed. Obviously this means replacing your existing web server if you don’t already use Caddy; however, you can also set it up as a reverse proxy, so that it only handles HTTPS for you, and your existing web server does the rest of the work (which might be useful if you have a lot of complex configuration in your existing setup).
The latest version of mod_md for Apache supports the TLS-ALPN-01 challenge. However, as of right now (late March 2019) this is still quite new and you may need to patch and compile some things to get it to work.
Some reverse proxies such as Nginx and recent versions of HAProxy can route traffic to different backends based on the TLS ALPN extension. You can use this feature to route the TLS-ALPN-01 challenges from Let’s Encrypt to an ACME client that supports that challenge - acme.sh provides some documentation on how to do this.
Another option that I was recently made aware of is uacme’s
ualpn which is a purpose-built mini-proxy that does just this and nothing else.
DNS-01 with CNAME records
If your DNS service doesn’t provide an API and you can’t simply switch to one that does, you can register another domain at a service with an API (or spin up your own using acme-dns), use a CNAME record to point the
_acme-challenge subdomain from your real domain to the new one, and use acme.sh’s DNS alias mode to get a certificate for the real domain while completing the challenge for the alternative one.