
A Wildcard SSL certificate for *.example.com covers every subdomain (blog.example.com, shop.example.com) under one certificate — but Let's Encrypt cannot issue wildcard certificates over HTTP-01. You must use the DNS-01 challenge.
This guide shows how to obtain a free Let's Encrypt Wildcard certificate using Certbot + DNS challenge, both manually and automatically via the Cloudflare DNS API, on an Ubuntu VPS — and how to auto-renew every 60 days.
Prerequisites: A domain whose DNS you control (the web server can be elsewhere), an Ubuntu VPS with sudo access. For the automatic path, create a Cloudflare API token with Zone:DNS Edit scope.
HTTP-01 vs DNS-01 Challenge
| Topic | HTTP-01 | DNS-01 |
|---|---|---|
| Verification | File at .well-known/acme-challenge/ | TXT record in DNS |
| Wildcard | ❌ Not supported | ✅ Supported |
| Needs Web Server | Yes — port 80 open | No |
| Auto Renew | Easy — Certbot handles it | Needs DNS API |
| Best for | Single-host certificates | Wildcards, internal services |
Why Wildcard SSL Is the Right Choice for Multi-Subdomain VPS Deployments
When your server hosts multiple subdomains — shop.example.com, blog.example.com, api.example.com, admin.example.com — issuing individual certificates for each one multiplies your management overhead and renewal schedule. A single wildcard certificate eliminates that complexity. Key advantages on a VPS:
- Single certificate pair — one
fullchain.pemand oneprivkey.pemshared across every Nginx virtual host. - Instant subdomain expansion — add a new subdomain by updating
server_namein Nginx and reloading. No certificate request needed. - No extra IP required — SNI (Server Name Indication) lets multiple HTTPS virtual hosts share one IP, and the wildcard covers all of them.
- Completely free — Let's Encrypt issues wildcard certificates at no cost, compared to paid wildcards that start at several thousand baht per year.
- Hands-free renewal — with the Cloudflare plugin configured, the systemd timer renews the certificate every 60 days without any manual intervention.
The key limitation to keep in mind: wildcards cover exactly one subdomain level. *.example.com covers a.example.com but not b.a.example.com. For sub-subdomains you need a separate wildcard such as *.a.example.com or a multi-SAN certificate.
Path 1 — Manual DNS Challenge (One-Off)
Use this if you only need it occasionally and your DNS has no API — but expect to paste a TXT record every 90 days at renewal.
1.1 Install Certbot
sudo apt update sudo apt install -y certbot
1.2 Request a Wildcard Certificate Manually
sudo certbot certonly --manual --preferred-challenges=dns \ --email [email protected] --agree-tos --no-eff-email \ -d example.com -d "*.example.com"
Certbot prints a TXT record to deploy, e.g.
Please deploy a DNS TXT record under the name: _acme-challenge.example.com. with the following value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Add that TXT record in your DNS panel (Cloudflare / Route 53 / DirectAdmin DNS), wait 1–2 minutes for propagation, then press Enter. Certbot verifies and issues the certificate under /etc/letsencrypt/live/example.com/, covering both the root and every subdomain.
Path 2 — Automatic via Cloudflare DNS Plugin
If your DNS is on Cloudflare, Certbot can issue and renew without any manual intervention.
2.1 Install the Cloudflare Plugin
sudo apt install -y python3-certbot-dns-cloudflare
2.2 Create a Cloudflare API Token
Go to My Profile → API Tokens → Create Token → Edit zone DNS, select the zone, and save the token.
2.3 Store the Token (Important: chmod 600)
sudo mkdir -p /etc/letsencrypt/cloudflare sudo nano /etc/letsencrypt/cloudflare/credentials.ini
Contents:
dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE
sudo chmod 600 /etc/letsencrypt/cloudflare/credentials.ini
⚠️ The credentials file must be mode 600. Otherwise Certbot refuses to use it for security reasons.
2.4 Request the Wildcard Certificate
sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \ --dns-cloudflare-propagation-seconds 30 \ --email [email protected] --agree-tos --no-eff-email \ -d example.com -d "*.example.com"
Certbot calls Cloudflare's API to add and remove the TXT record automatically. The certificate lands in /etc/letsencrypt/live/example.com/.
Step 3 — Wire Up Nginx
In your Nginx server block:
server {
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 hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
root /var/www/example;
index index.html;
}Test and reload:
sudo nginx -t sudo systemctl reload nginx
Step 4 — Auto-Renew Every 60 Days
Certbot ships a systemd timer. Check it:
sudo systemctl list-timers | grep certbot
Dry-run a renewal:
sudo certbot renew --dry-run
Reload Nginx automatically after every renewal:
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Contents:
#!/bin/bash systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Configuring Nginx Virtual Hosts to Share One Wildcard Certificate
Once you have the wildcard certificate, you reference the same certificate files in every virtual host. Here is an example with two subdomains in separate config files:
# /etc/nginx/sites-available/shop.conf server { listen 443 ssl http2; server_name shop.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; root /var/www/shop; index index.html index.php; } # /etc/nginx/sites-available/blog.conf server { listen 443 ssl http2; server_name blog.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; root /var/www/blog; index index.html index.php; }
Both virtual hosts share the same certificate pair. When Certbot renews every 60 days, the renewal hook reloads Nginx automatically — you never need to touch the individual virtual host configs.
ls -la /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx
Troubleshooting Common Wildcard SSL Issues
The following table covers the most frequent errors encountered during DNS-01 issuance and renewal:
| Problem | Cause | Fix |
|---|---|---|
| DNS propagation timeout | TXT record not visible before Certbot checks | Increase --dns-cloudflare-propagation-seconds 60 |
| credentials.ini permission error | File permissions are not 600 | sudo chmod 600 credentials.ini |
| Rate limit exceeded | More than 5 failed orders per domain per week | Wait 7 days or use --staging when testing |
| Nginx not reloaded after renewal | Missing or non-executable renewal hook | Check chmod +x reload-nginx.sh |
Always test with the staging environment first to avoid hitting rate limits during development:
sudo certbot certonly --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \ --staging \ -d example.com -d "*.example.com"
Check Expiration
sudo certbot certificates
# or
sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -datesWildcard SSL Hardening Checklist
- Keep credentials.ini at permission 600
- Use a scoped Cloudflare API token (Zone:DNS Edit) — never the Global API Key
- Allow only
TLSv1.2 TLSv1.3in Nginx; disable TLS 1.0/1.1 - Configure Auto-Renew with a renewal hook to reload Nginx
- Periodically scan with AsiaGB SSL Server Test
- Wildcards cover one level only —
*.example.comcoversa.example.combut notb.a.example.com
Need a VPS for Web + Wildcard SSL?
AsiaGB VPS from 500 THB/month with full root access — open ports freely for Let's Encrypt and Nginx. Choose Thailand / Singapore locations.
View VPS Plans