Wildcard HTTPS the easy way: acme.sh + Let's Encrypt (DNS-01)
One certificate for every subdomain you will ever add: blog., tube.,
status. — all of them, from a single issuance and a single renewal. That
is the difference between one certificate to think about and one per service
to forget.
This is the written depth behind the video: what DNS-01 is, why a wildcard needs it, and the exact flow with acme.sh and Let’s Encrypt.
What we end up with
A valid, auto-renewing certificate for tall.nu + *.tall.nu, proven with
openssl:
openssl x509 -in /etc/ssl/tall.nu/full.pem -noout -dates -subject -ext subjectAltName
# ...
# X509v3 Subject Alternative Name:
# DNS:*.tall.nu, DNS:tall.nu
Why DNS-01
Wildcard certificates require the DNS-01 challenge. HTTP-01 proves you
control one specific host by putting a file on it — it can never prove you
control * (every host at once). DNS-01 instead places a TXT record in the
zone, and a record in the zone covers the whole domain.
Note that wildcards are only one level deep: *.tall.nu covers
blog.tall.nu, but not x.blog.tall.nu.
The flow, end to end
Against the Let’s Encrypt staging issuer while you get it right (staging has its own, much higher rate limits):
export PATH="$HOME/.acme.sh:$PATH"
curl https://get.acme.sh | sh -s email=you@example.com
install -D -m 0644 dns_nmu.sh ~/.acme.sh/dnsapi/dns_nmu.sh
export NMU_API_TOKEN="$(cat /root/.nmu_token)" # never in frame
acme.sh --issue --dns dns_nmu -d tall.nu -d '*.tall.nu' --staging
mkdir -p /etc/ssl/tall.nu
acme.sh --install-cert -d tall.nu \
--key-file /etc/ssl/tall.nu/priv.key \
--fullchain-file /etc/ssl/tall.nu/full.pem \
--reloadcmd "systemctl reload apache2"
acme.sh reads the provider credential from the environment on first use and
saves it to ~/.acme.sh/account.conf, so renewals run unattended. Don’t paste
the token into your shell history and don’t hardcode it in the plugin — read it
from a file (as above), and keep it out of anything you record or commit.
The wildcard trap: two TXT values, one name
This is the part that trips people up. Both tall.nu and *.tall.nu publish
their challenge at the same name — _acme-challenge.tall.nu — so two TXT
values must exist at the same time during validation.
If your provider’s API replaces the record set on every write (many do), a
naive add-hook will overwrite the first value with the second and validation
will hang forever, repeating Checking tall.nu for _acme-challenge.tall.nu.
The fix is to accumulate: send the union of the values for the run. In our
dns_nmu hook that means keeping the values in a small state file and sending
the whole list each time.
Renewal is the point
Let’s Encrypt certificates last 90 days. acme.sh installs a cron job when it
installs itself, and --install-cert --reloadcmd makes the web server pick up
the new certificate after every renewal:
35 5,11,17,23 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
A certificate you have to renew by hand is a certificate that will one day expire.
Pitfalls
- HTTP-01 cannot validate a wildcard — it has to be DNS-01.
- Test against the staging issuer; production has tight rate limits.
- Keep the API credential least-privilege and out of history.
acme.shinstalls to~/.acme.shand is not onPATH— add it, or call it by full path.--reloadcmdmust actually reload the server, or the old certificate keeps being served until the next restart.
Watch the video: Wildcard HTTPS the easy way.