Let’s Encrypt Wildcard SSL with Certbot DNS-01 on Nginx

by Fahim

If you’ve ever tried running standard Certbot HTTP-01 verification for a wildcard like *.example.com, you know it fails instantly. Let’s Encrypt flat-out refuses HTTP validation for wildcards because spinning up a file on port 80 doesn’t prove you own the entire namespace—you have to use the DNS-01 validation challenge instead.

Setting this up means getting Certbot to talk directly to your DNS provider’s API to handle TXT record verification under the hood. Here is how to install the right plugins, issue wildcard certs, configure Nginx, and make sure renewals reload Nginx automatically without manual intervention.

Terminal screen showing successful Let's Encrypt wildcard certificate generation with Certbot on Nginx
Terminal screen showing successful Let's Encrypt wildcard certificate generation with Certbot on Nginx

Why HTTP-01 Fails for Wildcard Domains

With a standard HTTP-01 challenge, Certbot dumps a temporary token into .well-known/acme-challenge/ and Let’s Encrypt pulls it over port 80. That works fine for standalone hostnames, but it falls apart for wildcards because arbitrary or future subdomains might not route to an active web server yet.

DNS-01 solves this by creating an _acme-challenge.example.com TXT record with a cryptographic digest. Let’s Encrypt queries your authoritative nameservers directly. If you hit standard validation headaches on single hostnames instead, check out how to fix Certbot ACME HTTP-01 challenge failed errors on Nginx.

Step 1: Install Certbot and the DNS Plugin

On modern Ubuntu and Debian boxes, install Certbot via Snap. The OS package managers often ship outdated DNS plugins that choke on updated provider APIs. If your domain sits on Cloudflare, DigitalOcean, Route53, or Namecheap, automated plugins make this seamless.

Wipe any old APT certbot packages first, then install the core Certbot snap:

sudo apt remove certbot -y
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok

Next, install the specific DNS plugin for your provider (using Cloudflare or RFC2136 as examples here):

sudo snap install certbot-dns-cloudflare
sudo snap install certbot-dns-rfc2136

Step 2: Configure Provider API Credentials

Automating DNS-01 requires an API token with strictly scoped permissions to edit DNS records for your zone. Never use global account keys for this—stick to zone-scoped tokens.

Create a dedicated directory and config file for your credentials:

sudo mkdir -p /etc/letsencrypt/credentials
sudo nano /etc/letsencrypt/credentials/cloudflare.ini

Drop your API token into the INI file:

# Cloudflare API token with Zone:DNS:Edit permissions
dns_cloudflare_api_token = 8f7e6d5c4b3a2109fedcba9876543210abcdef12

Lock down the file permissions immediately so unprivileged users can’t read the token:

sudo chmod 600 /etc/letsencrypt/credentials/cloudflare.ini

If you manage your routing through Namecheap, double-check your delegation by checking our guide to configure root domains in Namecheap DNS with A records vs CNAME.

Step 3: Issue the Wildcard Certificate

To cover both the apex domain (example.com) and any subdomain (*.example.com), pass both explicitly using separate -d flags. Let’s Encrypt treats *.example.com as valid only for subdomains, not the root apex.

Run the issuance command:

sudo certbot certonly  --dns-cloudflare  --dns-cloudflare-credentials /etc/letsencrypt/credentials/cloudflare.ini  --dns-cloudflare-propagation-seconds 30  -d example.com  -d "*.example.com"  --agree-tos  --email admin@example.com  --no-eff-email

Certbot places your certificates and keys in /etc/letsencrypt/live/example.com/:

  • fullchain.pem: The server certificate bundled with Let’s Encrypt’s intermediate certs.
  • privkey.pem: Your private key (keep this locked down).
  • cert.pem and chain.pem: Individual certificate components.

Alternative: Issue Wildcards via Manual Hook Scripts

If your DNS provider doesn’t have an official Certbot snap plugin, you don’t have to manage TXT records by hand every 60 days. Write small auth and cleanup hook scripts that hit your registrar’s REST API using cURL.

Create an authentication script at /usr/local/bin/certbot-dns-auth.sh:

#!/usr/bin/env bash
# Certbot passes $CERTBOT_DOMAIN and $CERTBOT_VALIDATION API_KEY="your_secret_api_key"
RECORD_NAME="_acme-challenge.${CERTBOT_DOMAIN}"
RECORD_VALUE="${CERTBOT_VALIDATION}" # Call registrar API to create TXT record
curl -s -X POST "https://api.yourdns.com/v1/zones/${CERTBOT_DOMAIN}/records"  -H "Authorization: Bearer ${API_KEY}"  -H "Content-Type: application/json"  -d "{"type":"TXT","name":"${RECORD_NAME}","content":"${RECORD_VALUE}","ttl":60}" # Allow DNS servers time to propagate
sleep 30

Make the script executable:

sudo chmod 700 /usr/local/bin/certbot-dns-auth.sh

Tell Certbot to use the manual authenticator hooked into your custom script:

sudo certbot certonly  --manual  --manual-auth-hook /usr/local/bin/certbot-dns-auth.sh  --preferred-challenges dns-01  -d example.com  -d "*.example.com"  --agree-tos  --email admin@example.com

Check the Certbot Pre and Post Validation Hooks documentation for available environment variables during validation runs.

Step 4: Configure Nginx to Use the Wildcard Certificate

With certificates generated, update your Nginx server block to handle both root and wildcard subdomain routing over HTTPS with HTTP/2.

Open your site configuration:

sudo nano /etc/nginx/sites-available/wildcard.conf

Add the virtual host block:

server { listen 80; listen [::]:80; server_name example.com *.example.com; # Redirect all plain HTTP traffic to HTTPS return 301 https://$host$request_uri;
} server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com *.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Recommended TLS settings ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; root /var/www/html; index index.html index.php; location / { try_files $uri $uri/ =404; }
}

Test the configuration and reload Nginx:

sudo ln -s /etc/nginx/sites-available/wildcard.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

If you’re proxying traffic back to Python or Node.js backends behind Nginx, review how to deploy FastAPI and React on Hostinger VPS with Namecheap DNS for clean reverse proxy structures.

Step 5: Test Automated Certificate Renewal

Certbot installs a systemd timer by default, but DNS-01 renewals need a reload hook so Nginx actually picks up the new certificate files after renewal.

First, test that the automated renewal works without touching real certs:

sudo certbot renew --dry-run

Once the dry run passes, drop an executable deploy hook into /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh:

sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo bash -c 'cat < /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/usr/bin/env bash
systemctl reload nginx
EOF'
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Verify that the systemd renewal timer is active:

systemctl list-timers | grep certbot

Troubleshooting Common DNS-01 Failures

If your validation runs hit a wall, it’s usually one of these culprits:

  • DNS Propagation Delays: Some nameservers take longer than Certbot’s default 10-30 seconds to replicate TXT records globally. Add --dns-cloudflare-propagation-seconds 60 (or 120) to give your nameservers time to sync.
  • Multi-Level Subdomains: A wildcard cert for *.example.com only covers single-level subdomains like app.example.com. It will not cover api.v1.example.com. You need a separate SAN like *.v1.example.com for nested levels.
  • Stale TXT Records: If a previous manual run failed, an old _acme-challenge TXT record might be lingering. Clear it out in your DNS control panel before trying again. See the Let’s Encrypt DNS-01 Challenge Specifications for protocol details.
  • Origin IP Exposure: Don’t leave your origin server exposed while routing subdomains. If you sit behind edge proxies, follow our guide to restrict Nginx access to CDN origin shield IPs.

Frequently Asked Questions

Can I secure multiple levels of subdomains with one wildcard certificate?

No. Per TLS RFC specs, a wildcard only covers one label level. *.example.com covers auth.example.com, but not auth.stage.example.com. You must add separate entries for each level.

How often does Certbot renew wildcard certificates?

Let’s Encrypt certs last 90 days. Certbot’s timer checks twice daily and kicks off renewal attempts when certs are within 30 days of expiry.

Do I need to keep port 80 open if I use DNS-01?

No. DNS-01 verification works strictly over DNS queries to your nameservers. You don’t need port 80 open for Certbot to succeed, though leaving port 80 up to redirect HTTP requests to HTTPS is still recommended.

Next Steps

With your wildcard SSL certificates active on Nginx, make sure your backend services remain online and stable across server restarts. Read our guide on how to configure PM2, startup scripts, and log rotation on VPS to keep your application processes resilient.

all_in_one_marketing_tool