Back to blog
InfraJune 3, 20255 min read

Wildcard SSL Certs on a Cloudflare Domain With Certbot

One cert for all your subdomains. Setup takes 15 minutes and renewal is automatic. Here is exactly how to do it.

If you run multiple subdomains off a single domain (docs.yourdomain.com, api.yourdomain.com, media.yourdomain.com etc), managing individual Let's Encrypt certs for each one gets tedious fast. A wildcard cert covers *.yourdomain.com with a single certificate and a single renewal process.

The catch is that wildcard certs require DNS validation rather than HTTP validation. You cannot prove domain ownership by placing a file on a web server because a wildcard cert covers subdomains that might not even have a web server. Instead, you create a TXT record in your DNS to prove you control the domain.

With Cloudflare DNS, this can be fully automated using the certbot-dns-cloudflare plugin.

Setup

Install certbot and the plugin:

bash
sudo apt install certbot
pip install certbot-dns-cloudflare --break-system-packages

Create a Cloudflare API token. Go to Cloudflare dashboard > Profile > API Tokens > Create Token. Use the "Edit zone DNS" template and scope it to your specific zone. Save the token.

Create the credentials file:

bash
mkdir -p ~/.secrets
cat > ~/.secrets/cloudflare.ini << EOF
dns_cloudflare_api_token = your_api_token_here
EOF
chmod 600 ~/.secrets/cloudflare.ini

The chmod 600 is important. Certbot will refuse to use the credentials file if it is world-readable.

Getting the cert

bash
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d yourdomain.com \
  -d *.yourdomain.com \
  --agree-tos \
  --email [email protected]

The process takes about 30-60 seconds. Certbot creates a TXT record in your DNS automatically, waits for it to propagate, validates it, gets the cert, then cleans up the TXT record.

Certs are saved to /etc/letsencrypt/live/yourdomain.com/.

nginx config

Point your nginx SSL directives at the wildcard cert:

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

You can use the same cert block in every server block that handles subdomains. nginx handles it correctly.

Auto-renewal

Certbot installs a systemd timer or cron job automatically. Check that it is set up:

bash
systemctl status certbot.timer
# or
crontab -l | grep certbot

If you want to also reload nginx after renewal (to pick up the new cert), add a deploy hook:

bash
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << EOF
#!/bin/bash
nginx -s reload
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

This runs automatically after every successful renewal. The cert renews 30 days before expiry, so you should never see an expired cert in production.

More articles