Technology

The Hands‑Off Guide to Let’s Encrypt Wildcard SSL: DNS‑01, Auto‑Renew, and Real‑World Setups on cPanel, Plesk, and Nginx

So there I was, staring at yet another expiring SSL warning for a client who swears they “only launched two subdomains.” By the time we finished listing them, there were nine. Marketing had spun up a few launches. The mobile team had a staging area. Someone added a “beta.” You know how it goes. That was the day I stopped playing whack‑a‑mole with single‑host certificates and moved every domain with even a hint of subdomain sprawl to a Let’s Encrypt wildcard SSL with DNS‑01 validation and automation. It felt like unclogging a drain you’ve been ignoring for months—quiet relief and the kind of calm you don’t realize you’ve needed.

If you’ve ever had that moment where a subdomain goes red in the browser at 2 a.m., this one’s for you. In this guide, we’ll walk through the what and why of DNS‑01, then set up fully automated wildcard SSL in three common environments: cPanel, Plesk, and Nginx on a VPS. I’ll share what tends to break (and how I fix it fast), where to stash API tokens safely, and how to make renewals boring—which is the dream. By the end, you’ll have a game plan that keeps *.yourdomain.com locked down without late‑night fire drills.

Why Wildcard SSL + DNS‑01 Is the Sanity Saver

Let’s start with the heartbeat of the setup: wildcard certificates. A Let’s Encrypt wildcard like *.example.com covers all first‑level subdomains—think www, app, staging, beta. If your world changes a lot (and whose doesn’t?), you skip the endless add‑remove cycle of hostnames. The catch is that Let’s Encrypt only lets you get wildcards via DNS‑01, not the usual HTTP‑01. That’s their way of saying “prove you control the domain at the DNS layer,” which is more authoritative than dropping a file on a web server.

Here’s the thing: once you automate DNS‑01, renewals become gloriously boring. No manual TXT pasting, no calendar reminders. Your DNS provider’s API adds and removes the validation TXT records on schedule, Let’s Encrypt issues or renews, and your server installs the new cert. That’s it. I’ve run these pipelines for years across different stacks, and when they’re set right, they become invisible.

If you’re newer to SSL in general and want a refresher, I’ve written a friendly explainer about what an SSL certificate actually does and how it secures your site. It pairs nicely with this guide.

DNS‑01 Without the Headache: What’s Really Happening

Let’s demystify DNS‑01 in normal language. When you request a certificate, the CA (Let’s Encrypt) asks you to create a special TXT record at _acme-challenge.example.com. That TXT value is a unique token. If they can look up that record and it matches, they know you control the domain. For a wildcard, the validation still lives at _acme-challenge.example.com, even though you’re requesting *.example.com.

The smart path is to use automation so your server (or a CLI tool) talks to your DNS provider’s API and drops that TXT record just‑in‑time. After validation, it removes the record or leaves it be—depends on the tool. That’s where utilities like Certbot’s DNS plugins and acme.sh shine. They speak to popular DNS services and take care of the fussy parts for you.

If TXT records, CNAMEs, and AAAA glue your brain into knots, I put together my friendly guide to DNS records and the little gotchas. It will absolutely make the next section feel clearer.

cPanel: From “Please Renew” Emails to Fully Automated Wildcards

I’ll be honest: cPanel out of the box does a beautiful job with standard HTTP‑01 “AutoSSL,” but wildcard via Let’s Encrypt needs DNS‑01, and not every hosting provider wires that up for you. The reliable workaround I use is to pair a DNS‑aware ACME client with cPanel’s API for installation. Once this is dialed in, it hums along elegantly.

The high‑level flow I use

First, I pick a DNS automation tool. I use acme.sh when I want a tiny, dependency‑light script with dozens of DNS providers supported; I use Certbot when I want a more standard, distro‑friendly approach. Then I wire the tool to my DNS provider via API tokens. The tool requests a wildcard from Let’s Encrypt, creates the TXT record via API, Let’s Encrypt validates, and the certificate gets issued. Finally, I deploy the cert directly into cPanel using its UAPI or a deploy‑hook. The whole loop becomes automatic with a cron job.

Option A: acme.sh with your DNS provider’s API

acme.sh is tiny, fast, and knows a shocking number of DNS APIs. I’ve used it with Cloudflare, Route 53, DigitalOcean, GoDaddy—you name it. Here’s a sketch of what it looks like with Cloudflare. The dance is similar for other providers; you just change the environment variables.

# install acme.sh (as the cPanel user or root; I prefer per-user)
curl https://get.acme.sh | sh -s [email protected]

# export your DNS provider credentials (use least-privileged tokens!)
export CF_Token="<cloudflare_api_token_with_dns_edit>"
export CF_Account_ID="<your_account_id>"

# issue the wildcard + apex
~/.acme.sh/acme.sh --issue 
  --dns dns_cf 
  -d example.com -d '*.example.com'

# optional: use the Let's Encrypt staging endpoint while testing
# ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
# ~/.acme.sh/acme.sh --issue --dns dns_cf -d example.com -d '*.example.com' --staging

Once issued, you can deploy to cPanel via the built‑in deploy hook. acme.sh has a cpanel_uapi deploy target that talks to cPanel’s API and installs the certificate for the correct domain/user. You’ll need a cPanel API token and the cPanel username.

# store your cPanel API token securely and set environment variables
export DEPLOY_CPanel_USER="<cpanel_username>"
export DEPLOY_CPanel_TOKEN="<long_api_token>"
export DEPLOY_CPanel_SERVER="https://<hostname_or_domain>:2083"  # or 2087 for WHM

# deploy the cert into cPanel
~/.acme.sh/acme.sh --deploy 
  -d example.com 
  --deploy-hook cpanel_uapi

# renewals: acme.sh creates a cron entry; verify it exists
~/.acme.sh/acme.sh --cron --home ~/.acme.sh

In my experience, this approach is the sweet spot: you don’t have to move DNS, you keep your preferred provider, and the install lands right where cPanel expects it. Renewals run quietly, and your certificate stays fresh without you lifting a finger.

Option B: Certbot with a DNS plugin, then install via cPanel UAPI

Certbot can do the same, with the added comfort of being the “official” Let’s Encrypt client many distros ship. The trick is using a DNS plugin like Cloudflare, Route 53, or DigitalOcean. After issuance, you call cPanel’s UAPI to install the cert. If you’re comfortable with yum/apt packages and systemd timers, this feels very native.

# example with Cloudflare on a CentOS/Alma/Rocky server
sudo dnf install certbot python3-certbot-dns-cloudflare -y

# credentials file for Cloudflare
sudo bash -c 'cat > /etc/letsencrypt/cloudflare.ini <<EOF
dns_cloudflare_api_token = <token_with_dns_edit>
EOF'
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

# request the wildcard and apex
sudo certbot certonly 
  --dns-cloudflare 
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini 
  -d example.com -d '*.example.com' 
  -m [email protected] --agree-tos --non-interactive

# install into cPanel via UAPI (adjust --user and domain)
uapi --user=<cpanel_username> SSL install_ssl 
  cert="$(sudo cat /etc/letsencrypt/live/example.com/cert.pem)" 
  key="$(sudo cat /etc/letsencrypt/live/example.com/privkey.pem)" 
  cabundle="$(sudo cat /etc/letsencrypt/live/example.com/chain.pem)"

Certbot sets up a systemd timer for renewal automatically. I add a deploy hook that calls UAPI again on renewal so the fresh certs install themselves.

# reload or re-install on renewal
sudo bash -c 'cat > /etc/letsencrypt/renewal-hooks/deploy/10-install-cpanel.sh <<EOF
#!/usr/bin/env bash
uapi --user=<cpanel_username> SSL install_ssl 
  cert="$(cat "$RENEWED_LINEAGE/cert.pem")" 
  key="$(cat "$RENEWED_LINEAGE/privkey.pem")" 
  cabundle="$(cat "$RENEWED_LINEAGE/chain.pem")"
EOF'
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/10-install-cpanel.sh

Either way, test in staging first. I once chased a DNS permission issue for an hour only to realize I was hitting production rate limits because I forgot the staging flag. Don’t be me on a Friday.

Plesk: Built‑In Wildcard, If DNS Is in the Right Place

Plesk has a nice edge here: its Let’s Encrypt extension understands DNS‑01 and can handle wildcard certificates directly in the UI. The smoothest ride happens when your DNS is hosted in Plesk (or synced from Plesk to a cloud DNS via an extension). In that case, Plesk adds and cleans up the TXT record for you during both issuance and renewal.

When DNS lives in Plesk

From the domain’s dashboard, open the Let’s Encrypt extension, check the option to issue a wildcard, include both example.com and *.example.com, and proceed. Plesk drops the TXT record into its DNS zone automatically, Let’s Encrypt validates, and you’re done. Renewals will just happen. It’s the kind of “set it and forget it” we all need more of.

When DNS lives elsewhere

If your DNS is on Route 53, Cloudflare, or another provider, you have two good paths. One is to momentarily add the TXT record manually when prompted—fine for one‑offs, not great for automation. The other is to use a Plesk DNS extension that syncs zones to your provider. For example, there are extensions for Route 53 and Google Cloud DNS. Once configured, Plesk updates those zones on your behalf, which lets the wildcard flow be fully automated again.

A quick tip: mind your TTLs. If you’re adding TXT records manually or syncing zones, a high TTL can slow validation down. I have a whole playbook on this in my TTL Playbook for zero‑downtime migrations and faster DNS changes. It’s the same trick I use to speed up ACME validation in tricky environments.

Renewals and the “invisible” factor

What I love about Plesk’s approach is that, once the pipeline is healthy, renewal day looks the same as any other day. No banners, no nagging. The only times I’ve seen hiccups were when DNS got moved without telling Plesk, or when a provider changed API auth. I keep API tokens well‑scoped and rotate them on a schedule. It’s a small habit that saves big headaches.

Nginx on a VPS: Certbot or acme.sh, and a Clean Reload

For a straight VPS running Nginx, I choose between Certbot and acme.sh based on two things: what the OS packages nicely, and which DNS provider I need to talk to. Both work brilliantly.

Certbot example with a DNS plugin (Cloudflare)

Certbot’s DNS plugins are straightforward and well‑documented. With Cloudflare, an API token limited to one zone’s DNS edit permission is enough.

sudo apt update && sudo apt install -y certbot python3-certbot-dns-cloudflare

# credentials file (strict permissions)
sudo bash -c 'cat > /etc/letsencrypt/cloudflare.ini <<EOF
dns_cloudflare_api_token = <token>
EOF'
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

sudo certbot certonly 
  --dns-cloudflare 
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini 
  -d example.com -d '*.example.com' 
  -m [email protected] --agree-tos --non-interactive

# point Nginx to the certs
sudo sed -i 's|# ssl_certificate .*|ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;|' /etc/nginx/sites-available/example
sudo sed -i 's|# ssl_certificate_key .*|ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;|' /etc/nginx/sites-available/example

# safer to edit by hand, of course, then test and reload
sudo nginx -t && sudo systemctl reload nginx

For renewals, Certbot’s systemd timer handles the schedule. I add a deploy hook so Nginx reloads only when a cert actually changes. It’s tiny but satisfying.

sudo bash -c 'cat > /etc/letsencrypt/renewal-hooks/deploy/20-reload-nginx.sh <<"EOF"
#!/usr/bin/env bash
/usr/sbin/nginx -t && /bin/systemctl reload nginx
EOF'
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/20-reload-nginx.sh

acme.sh example with Route 53

If you live in the Amazon world, acme.sh’s Route 53 integration is a delight. Set your AWS keys with least privilege (only the specific zone) and let acme.sh do the rest.

curl https://get.acme.sh | sh -s [email protected]

export AWS_ACCESS_KEY_ID="<access_key>"
export AWS_SECRET_ACCESS_KEY="<secret_key>"

~/.acme.sh/acme.sh --issue 
  --dns dns_aws 
  -d example.com -d '*.example.com'

# install to a path Nginx uses
sudo mkdir -p /etc/nginx/ssl/example.com
~/.acme.sh/acme.sh --install-cert -d example.com 
  --fullchain-file /etc/nginx/ssl/example.com/fullchain.pem 
  --key-file /etc/nginx/ssl/example.com/privkey.pem 
  --reloadcmd "nginx -t && systemctl reload nginx"

Now renewals are automatic via acme.sh’s cron job, and Nginx reloads on change. If you ever wonder whether renewals are running, check your logs—Certbot uses systemd journal; acme.sh logs under its home directory. I like to set a calendar reminder to glance at them monthly. Five minutes well spent.

Little Things That Make the Whole Setup Rock‑Solid

These are the small habits that have saved me hours across dozens of deployments.

Use staging while you wire it up

Let’s Encrypt has rate limits, and you’ll hit them faster than you think when you’re experimenting. Switch to the staging endpoint until your pipeline works end‑to‑end. With Certbot, that’s –dry-run or the staging server; with acme.sh, use –staging or set the default CA to staging first.

Mind your CAA records

If you use CAA, make sure your domain allows Let’s Encrypt to issue wildcards. Add an issue or issuewild record permitting letsencrypt.org. Otherwise, you’ll have a perfect pipeline that fails at the very last step. If you need a refresher on DNS safety basics, I break down what DNSSEC is and how it protects you, plus the pitfalls that can block validation if misconfigured.

Keep DNS API tokens tight and quiet

Scope tokens to a single zone with DNS edit only. Store them in root‑readable files with 600 permissions. I’ve seen renewals fail because a token was rotated and nobody updated the server. A tiny secrets checklist (where tokens live, when they expire) prevents that “why did this break?” mystery later on.

Low TTLs help “manual” paths

If you’re temporarily adding TXT records by hand, lower the TTL beforehand so you’re not waiting on slow propagation. When you’re done, set it back. If this is new to you, my TTL Playbook for zero‑downtime migrations shows exactly how I approach this without breaking a sweat.

Chain files and reloads

Browsers and some middleware can be picky about intermediate chains. Always point servers at the fullchain file and not just the leaf certificate. After renewal, reload the service cleanly. With Nginx, I like “test then reload” because a failed config push should never take down a running site.

Don’t forget the rest of your security posture

SSL is one layer. Once you have it, round out the basics: set HSTS and other headers, keep redirects tight, and avoid mixed content. If you want a plain‑English walkthrough, here’s how I set strong HTTP security headers without breaking stuff. It’s the natural next step once TLS is in place.

Common “Why Is This Failing?” Moments (And Quick Fixes)

I’ve met every error you can imagine. Here’s how I think through them in the moment.

“Validating the TXT record is failing”

Nine times out of ten, this is either a propagation lag or the TXT record landing in the wrong zone. Confirm the TXT value and name, double‑check you don’t have an extra domain suffix (e.g., _acme-challenge.example.com.example.com), and query a public resolver. If you’re using multiple DNS providers or moved zones recently, make sure the NS records point where you think they do.

“It worked yesterday, now renewals are broken”

Tokens expire, permissions change, or DNS moved. I look at the renewal logs first, then test the DNS API call directly. If that works, I run the ACME client in verbose or debug mode. You’ll often see a clear 401 from your DNS provider—time to rotate and update the credentials file. And if you switched DNS providers, ensure your automation uses the right plugin for the new home.

“The site still shows the old certificate”

This usually means the certificate issued fine but never made it into the web server, or the service didn’t reload. On cPanel, check the UAPI call or the acme.sh deploy hook logs. On Nginx, verify the live paths and perform a manual reload. I also peek at any L4/L7 proxies in front of the site—CDNs can cache certs too.

“Wildcard works, but the apex domain is missing”

Remember that wildcard *.example.com does not cover example.com. Always include both the apex and the wildcard in your request. It’s a small oversight that happens to everyone once.

“We use DNSSEC—does it matter for ACME?”

It matters in the best way when configured right. Signed zones help protect the integrity of your TXT validation. Misconfigured DNSSEC, however, can block resolution. If validation mysteriously fails yet your TXT record exists, I immediately test DNSSEC health and check the DS record chain. If you need a refresher, I’ve got a friendly explainer on how DNSSEC works and why it matters.

Putting It All Together: A Quick, Real‑World Blueprint

If you only take one recipe from this post, make it this: pick a DNS‑aware ACME tool, use least‑privilege API tokens, include both apex and wildcard, point your server at the full chain, and add a reload hook. That’s the heart of the whole system, no matter where you host.

Here are the official docs I lean on for the nitty‑gritty details and new plugin support. The explanations are clean, and the examples are current:

• The Let’s Encrypt documentation on wildcard certificates for the DNS‑01 basics and caveats.
• The Certbot documentation about DNS plugins to pick your provider module and credential format.
• The acme.sh project documentation for provider variable names and deploy hooks.

That trio covers 95% of what I encounter in the wild.

Wrap‑Up: Make Renewals Boring Again

I still remember the first time a big renewal wave came and went with zero alerts, zero pings, zero anything. I checked logs a little suspiciously and then laughed—it had all just worked. That’s what a good DNS‑01 wildcard setup gives you: calm. You aren’t chasing new subdomains, you aren’t pasting TXT records, and you aren’t shipping last‑minute hotfixes because someone forgot a renewal.

If you’re on cPanel, wire up acme.sh or Certbot with your DNS provider and install via UAPI. On Plesk, lean into the Let’s Encrypt extension and keep DNS where Plesk can manage it (or sync it). On Nginx, choose Certbot or acme.sh, point at fullchain, and reload on change. Add staging while you experiment, keep API tokens tight, and make a habit of peeking at renewal logs once in a while. And when you’re ready to go the extra mile, set up security headers—the finishing touch after TLS is solid. I shared exactly how I do that in my guide on getting HSTS, CSP, and friends right.

Hope this was helpful! If there’s a stack you want me to cover next—HAProxy, Traefik, Kubernetes Ingress—I’m game. Until then, may your certificates auto‑renew quietly and your dashboards stay gloriously boring.

Frequently Asked Questions

Yes, wildcard certificates from Let’s Encrypt are free, but they require the DNS‑01 challenge. That means your automation needs access to your DNS provider’s API to add TXT records during issuance and renewal.

You do. A wildcard covers subdomains like app.example.com, but not example.com itself. Always request both example.com and *.example.com in the same certificate so browsers are happy everywhere.

Renewals will fail until the token is updated. Keep tokens with least privilege, store them with 600 permissions, and note their rotation dates. If renewals start failing, check the logs first, then test a simple DNS API call to confirm authentication.