If a project uses several subdomains — app.example.com, api.example.com, staging.example.com — issuing and renewing a separate certificate for each one gets tedious fast. A wildcard certificate covering *.example.com solves that with a single certificate, and when the domain's DNS is already on Cloudflare, Certbot's DNS-01 challenge makes issuing one straightforward.

Why a wildcard certificate

A wildcard cert means new subdomains don't need a new certificate request or a server restart to go live over HTTPS. For a site that spins up a staging subdomain per feature, or that adds a new subdomain per client, that's a real reduction in operational overhead.

Why DNS-01 instead of HTTP-01

The standard HTTP-01 challenge proves domain ownership by serving a file from the domain over port 80 — but it can only validate one hostname at a time, so it can't issue a wildcard. The DNS-01 challenge instead proves ownership by creating a specific TXT record, which works for wildcards and doesn't require port 80 to be reachable at all. With the domain's DNS already managed in Cloudflare, the certbot-dns-cloudflare plugin can create and remove that TXT record automatically.

Issuing the certificate

On the server, install Certbot along with the Cloudflare DNS plugin:

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

Create a credentials file with a scoped Cloudflare API token (Zone → DNS → Edit permission on the relevant zone is enough — avoid using a global API key):

sudo mkdir -p /etc/letsencrypt
sudo nano /etc/letsencrypt/cloudflare.ini
dns_cloudflare_api_token = your_scoped_api_token
sudo chmod 600 /etc/letsencrypt/cloudflare.ini

Request the certificate for both the root domain and the wildcard:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d example.com \
  -d '*.example.com'

Certbot calls the Cloudflare API to create the required TXT record, waits for it to propagate, validates it, then removes the record automatically.

Wiring it into Nginx

Point the relevant server blocks at the issued certificate and key:

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Test the configuration before reloading:

sudo nginx -t
sudo systemctl reload nginx

Renewal

Certbot installs a systemd timer (or cron job, depending on the distribution) that checks for renewal twice a day and only renews certificates within 30 days of expiry. Because the DNS-01 challenge doesn't need port 80, renewal works quietly in the background without any downtime. It's worth running a dry run after setup to confirm it's wired correctly:

sudo certbot renew --dry-run