You cannot issue a wildcard SSL certificate through the standard HTTP-01 webroot challenge. If you run a multi-tenant SaaS, a dynamic staging environment, or customer subdomains, Let’s Encrypt forces you to verify domain ownership via a DNS-01 TXT record.
I set this up last week on an Ubuntu 24.04 droplet running Nginx 1.24. Here is the exact walk-through to route any arbitrary subdomain to your Nginx apps, issue a dual-SAN wildcard certificate, and automate the renewals so your sites never drop dead after 90 days.

Why HTTP-01 Fails for Wildcard Domains
Standard Let’s Encrypt certificates use the HTTP-01 challenge. The ACME server writes a token into your web root at /.well-known/acme-challenge/ and makes an HTTP request to confirm you control the server. That works fine for single hosts like app.example.com or www.example.com.
Wildcards are different. Because a certificate for *.example.com can cover any prospective host name under your zone, the ACME protocol explicitly requires the DNS-01 challenge type. You prove domain control by publishing a cryptographic hash as a _acme-challenge TXT record in your DNS zone file.
If you attempt to run certbot --nginx -d "*.example.com", Certbot stops immediately with an error informing you that the client does not support HTTP validation for wildcard SANs. You must route through DNS.
Step 1: Point Your Wildcard DNS Record to Nginx
Before touching SSL, your server needs to intercept incoming traffic for undefined subdomains. Open your DNS provider dashboard and create an A record pointing an asterisk to your server’s public IPv4 address.
- Type: A
- Host / Name:
* - Value:
198.51.100.25(replace with your server IP) - TTL: 300 seconds (keep this low while testing)
If you manage your zone using Namecheap or an external registrar, check our guide on how to create a subdomain with Namecheap DNS to verify the zone format. Once saved, test that random hostnames resolve to your box using dig.
What I ran in my terminal:
dig +short ping-test.example.com @1.1.1.1
dig +short anything-goes.example.com @1.1.1.1Both queries should return your server’s IP address. If they return nothing, wait for DNS cache expiration before continuing.
Step 2: Install Certbot and DNS Credentials
Do not install Certbot via standard distro APT packages if they are outdated. The official EFF recommendation is Snap, or modern APT packages if your distro maintains current releases. I run Certbot on Ubuntu using Snap to ensure provider plugins match the core binary.
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotFor fully automated issuance and renewals, Certbot needs permission to create and delete TXT records on your DNS provider. Certbot maintains plugins for major DNS providers like Cloudflare, Route53, DigitalOcean, and Google Cloud. For Cloudflare, install the plugin using Snap:
sudo snap set certbot trust-plugin-with-submodules=ok
sudo snap install certbot-dns-cloudflareNext, restrict access to your API token. Create a credentials directory readable only by root:
sudo mkdir -p /etc/letsencrypt/secrets
sudo nano /etc/letsencrypt/secrets/dns-provider.iniAdd your scoped API token inside the file:
# Cloudflare API token with Zone:DNS:Edit permissions
dns_cloudflare_api_token = 9a8b7c6d5e4f3a2b1c0d_sample_tokenLock down file permissions so unprivileged system users cannot read your secret:
sudo chmod 600 /etc/letsencrypt/secrets/dns-provider.iniStep 3: Issue the Wildcard Certificate
Here is a common trap: if you request only *.example.com, your certificate will secure tenant1.example.com and dev.example.com, but browsers visiting the apex root example.com will throw an untrusted certificate error. You must issue the certificate with both the root apex and the wildcard in the Subject Alternative Name (SAN) list.
Run the request with your DNS plugin credentials file:
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/secrets/dns-provider.ini --dns-cloudflare-propagation-seconds 30 -d example.com -d "*.example.com" Certbot communicates with the DNS API, sets the _acme-challenge.example.com TXT record, waits 30 seconds for authoritative servers to sync, and requests validation from Let’s Encrypt. Upon success, Certbot writes the files to /etc/letsencrypt/live/example.com/.
If your DNS provider lacks a direct Certbot plugin, you can generate the certificate interactively using manual mode:
sudo certbot certonly --manual --preferred-challenges dns -d example.com -d "*.example.com"Certbot prints a specific hash and pauses. You open your DNS manager, add the TXT record, wait for propagation, and press enter. Manual mode works for initial testing, but it prevents hands-free auto-renewals. If you run into validation dropouts later on, check our guide to fixing Certbot auto-renewal failures.
Step 4: Configure the Wildcard Server Block in Nginx
Now wire the certificate into your Nginx configuration. Nginx natively supports wildcard matching in the server_name directive. You can also extract the subdomain name directly using regex named captures to route requests dynamically to separate user folders or backend ports.
Create a new configuration block in /etc/nginx/sites-available/wildcard.conf:
sudo nano /etc/nginx/sites-available/wildcard.confHere is a battle-tested config that handles HTTP-to-HTTPS redirection and routes subdomains to an upstream web service:
# HTTP: Redirect all subdomains and root to HTTPS
server { listen 80; listen [::]:80; server_name example.com *.example.com; return 301 https://$host$request_uri;
} # HTTPS: Wildcard Catch-all
server { listen 443 ssl; listen [::]:443 ssl; server_name ~^(?.+).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; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # Logging with subdomain tag access_log /var/log/nginx/wildcard_access.log combined; error_log /var/log/nginx/wildcard_error.log warn; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Subdomain $subdomain; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
} Notice the named regular expression: ~^(?.+).example.com$. This captures the subdomain prefix and passes it into your application inside the X-Subdomain HTTP header. This is how multi-tenant apps know which workspace to query in the database without parsing hostnames manually. Refer to the Nginx server names documentation for rules on regex matching precedence.
Enable the site configuration and test syntax:
sudo ln -s /etc/nginx/sites-available/wildcard.conf /etc/nginx/sites-enabled/
sudo nginx -tIf the test passes, reload Nginx:
sudo systemctl reload nginxIf your project runs a Node app, read our tutorial on how to deploy Express with PM2 and Nginx to verify socket and proxy forwarding.
Step 5: Testing and Hardening Auto-Renewals
Certbot certificates expire every 90 days. Because we used an authenticated DNS plugin, Certbot can renew the certificate in the background without user intervention. The Snap installation provides an automated systemd timer that checks renewals twice daily.
Run a dry run to verify the renewal cycle executes without hanging on DNS challenges:
sudo certbot renew --dry-runThe output should verify validation success without prompting for input:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Simulating renewal of an existing certificate for example.com and *.example.com
Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/example.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - When Certbot updates the disk files, Nginx holds the old certificates in memory until reloaded. Set up a deploy hook so Nginx restarts cleanly when a new certificate writes to disk. Edit /etc/letsencrypt/renewal/example.com.conf and verify the renew hook is set under [renewalparams]:
renew_hook = systemctl reload nginxAlternatively, place an executable script inside Certbot’s post-renewal hook directory:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/usr/bin/env bash
systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shGotchas: The Wildcard Traps That Broke My Setup
I hit two unexpected roadblocks while deploying this stack on production servers. Here is how to avoid them.
1. Nested Subdomains Are Not Covered
A standard wildcard certificate for *.example.com covers exactly one subdomain label. It matches beta.example.com and client.example.com. It does not match staging.client.example.com. If you need deep nesting, you must issue dedicated certificates for each level, like *.staging.example.com.
2. Split DNS Resolution and Propagation Delays
DNS changes do not appear across global nameservers instantaneously. When Certbot creates the TXT record, the ACME server queries authoritative nameservers directly—not your local recursive resolver. If your DNS provider has slow propagation between its primary and secondary authoritative servers, Certbot checks before the record is readable and fails.
If you see NXDOMAIN or Incorrect TXT record value errors during issuance, increase the propagation sleep parameter:
--dns-cloudflare-propagation-seconds 60Increasing this value from the default gives secondary nameservers sufficient time to sync.
3. DNS Zone Collisions With MX Records
If you point Namecheap or external DNS to your VPS using wildcard records, take care not to clobber existing mail records. See how to point Namecheap DNS without breaking email to preserve your MX and SPF configuration.
Wildcard SSL and Nginx FAQ
Can I use Let’s Encrypt wildcards with the standard Nginx webroot plugin?
No. Let’s Encrypt requires a DNS-01 challenge for all wildcard issuance. The webroot and HTTP-01 plugins only support single fully qualified domain names.
Does a wildcard certificate secure the apex domain?
No, not by default. A certificate containing only *.example.com will produce an SSL mismatch error on https://example.com. Always pass both -d example.com and -d "*.example.com" when running Certbot.
Can I point specific subdomains to a different server while using wildcards?
Yes. DNS resolution prioritizes exact matches over wildcards. If you add an explicit A record for mail.example.com pointing to IP 203.0.113.10, DNS clients route there. All unlisted names fall back to the * record pointing to your Nginx proxy.
How do I test my wildcard Nginx configuration before going live?
Add a temporary override to your local machine’s /etc/hosts file (or C:WindowsSystem32driversetchosts on Windows):
198.51.100.25 dev1.example.com dev2.example.comThis allows you to verify your Nginx routing and SSL handshake in your local browser before updating production DNS records.
Next Steps
Now that your server terminates wildcard traffic, you can automate tenant provisioning without touching Nginx configs again. If your application serves static files or dynamic content under heavy load, check our tutorial on how to configure reverse proxy caching and HTTP header policies to keep server latency low.

