Let's Encrypt DomainFactory

Some shared hosting packages with cPanel do not offer AutoSSL or only offer them at higher rates. The result: You cannot issue a free Let's Encrypt certificate via the cPanel interface, and all domains are stuck on a self-signed certificate. The browser displays the page as “not secure”. The way out: get the certificate yourself with acme.sh and install it via the cPanel UAPI - automatically renewing, without AutoSSL.


HTTPS has long been mandatory. It's all the more frustrating, then, when your own hosting package doesn't offer a convenient way to obtain a free certificate. This happens more often than you might think, for example with inexpensive entry-level packages or after a plan change where a certificate is suddenly no longer included. However, this gap can be elegantly and permanently closed with just a few lines of code in the shell – the complete process is described below.

1. Log in via SSH

ssh -p <port> <user>@example.com

2. Install acme.sh

curl https://get.acme.sh | sh -s email=me@example.com
source ~/.bashrc

3. Set Let's Encrypt as the CA

acme.sh --set-default-ca --server letsencrypt

4. Issue certificate

acme.sh --issue -d example.com -d www.example.com -w ~/public_html

5. Install into cPanel

acme.sh --deploy -d example.com --deploy-hook cpanel_uapi

6. Check

uapi SSL installed_hosts
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
     | openssl x509 -noout -issuer -dates

The hook writes the certificate to the appropriate vHost via UAPI. The deploy setting is saved - from now on the cron renews and installs everything independently before it expires. The issuer is “Let’s Encrypt” and the expiry date is around 90 days in the future. If the old, self-signed certificate appears instead, a hard reload in the browser usually helps - the previous certificate can be temporarily stored.

Some hosts run their own cPanel hook. install_ssl and responds with something like adminbin Cpanel/hooks2/...: exit 255. acme.sh still reports “successfully deployed” – and that is correct. In this case, the hook fails because of a subsequent step (e.g. an internal notify or sync job from the host), not because of the actual installation.

For domains with umlauts, acme.sh stores the certificate internally in the Punycode form (xn--…), while the hook's auto-matcher compares the Unicode form. Result: “deployed to 0 of 0 sites” – nothing is installed. The tricky thing is that acme.sh also reports “Success” here, so the error is easily overlooked. Solution: Work directly with the Punycode domain and turn off auto mode:

python3 -c "import sys;print(sys.argv[1].encode('idna').decode())" hallöle.de
acme.sh --issue -d xn--hallle-zxa.de -d www.xn--hallle-zxa.de -w ~/public_html
export DEPLOY_CPANEL_AUTO_ENABLED='false'
acme.sh --deploy -d xn--hallle-zxa.de --deploy-hook cpanel_uapi

With SSH access, you don't need AutoSSL: acme.sh issues the certificate, which cpanel_uapiThe -Hook installs it, and the included cron job keeps it automatically updated. Once set up, the free HTTPS service runs continuously on its own. Those who invest the initial effort save themselves every manual renewal in the future – and can extend the same solution to any additional domain in the same account with a single command.

Back