Wildcard SSL Certificates with Let's Encrypt

Wildcard SSL certificates secure multiple subdomains with a single certificate. Let's Encrypt provides free wildcard certificates through its ACME (Automated Certificate Management Environment) protocol. This guide covers obtaining wildcard certificates, DNS-based validation, automation with Certbot and Cloudflare, and renewal strategies.

Table of Contents

Prerequisites

Before obtaining wildcard certificates, ensure you have:

  • Domain name with DNS control
  • Certbot installed on your server
  • Root or sudo access
  • Understanding of DNS records
  • Web server (Nginx, Apache) configured
  • Access to DNS provider API (Cloudflare, Route53)

Understanding Wildcard Certificates

A wildcard certificate for *.example.com secures:

  • api.example.com
  • mail.example.com
  • cdn.example.com
  • shop.example.com
  • And all other subdomains

However, it does NOT secure example.com (base domain). For both, request a certificate for both *.example.com and example.com.

Wildcard certificates require DNS validation (CNAME/TXT record challenges) since multiple hosts can't all verify the same HTTP challenge.

Installing Certbot

Install Certbot on Ubuntu/Debian:

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx python3-certbot-apache

Install DNS plugin for your provider. For Cloudflare:

sudo apt-get install -y python3-certbot-dns-cloudflare

For Route53:

sudo apt-get install -y python3-certbot-dns-route53

For other providers, install the appropriate plugin:

# Generic installation
pip3 install certbot-dns-plugin-name

Verify Certbot installation:

certbot --version
certbot plugins

DNS Challenge Overview

Let's Encrypt validates wildcard domain ownership by requesting DNS TXT record modifications. The process:

  1. Certbot creates a unique authorization token
  2. Certbot creates a TXT record: _acme-challenge.example.com
  3. Let's Encrypt validates the TXT record exists
  4. Certificate is issued
  5. TXT record can be removed

For automated DNS updates, use DNS provider plugins.

Cloudflare DNS Integration

Integrate Certbot with Cloudflare for automated DNS validation. Create Cloudflare API credentials first.

Log in to Cloudflare dashboard and create an API token:

  1. Go to Account Settings > API Tokens
  2. Create Token > Edit zone DNS
  3. Grant permissions for specific zone
  4. Copy the token

Store credentials in ~/.cloudflare/cloudflare.ini:

mkdir -p ~/.cloudflare
nano ~/.cloudflare/cloudflare.ini

Add credentials:

dns_cloudflare_api_token = your_api_token_here

Set proper permissions:

chmod 600 ~/.cloudflare/cloudflare.ini

Request wildcard certificate with Cloudflare validation:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.cloudflare/cloudflare.ini \
  -d "*.example.com" \
  -d "example.com" \
  --agree-tos \
  --email [email protected]

Certbot will:

  1. Contact Let's Encrypt for challenge
  2. Create TXT record in Cloudflare DNS
  3. Validate the record
  4. Remove the TXT record
  5. Issue certificate

For production, use non-interactive mode:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.cloudflare/cloudflare.ini \
  -d "*.example.com" \
  -d "example.com" \
  --agree-tos \
  --email [email protected] \
  --non-interactive

Verify certificate installation:

ls -la /etc/letsencrypt/live/example.com/

Output should show:

cert.pem -> ../../../archive/example.com/cert1.pem
chain.pem -> ../../../archive/example.com/chain1.pem
fullchain.pem -> ../../../archive/example.com/fullchain1.pem
privkey.pem -> ../../../archive/example.com/privkey1.pem

Route53 Integration

For AWS Route53 DNS, create an IAM user with DNS permissions.

Create IAM policy for Route53:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:GetChange",
        "route53:ChangeResourceRecordSets",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/*",
        "arn:aws:route53:::change/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "route53:ListHostedZonesByName",
      "Resource": "*"
    }
  ]
}

Install certbot-dns-route53:

sudo apt-get install -y python3-certbot-dns-route53

Create AWS credentials file ~/.aws/credentials:

mkdir -p ~/.aws
nano ~/.aws/credentials

Add credentials:

[default]
aws_access_key_id = your_access_key
aws_secret_access_key = your_secret_key

Set permissions:

chmod 600 ~/.aws/credentials

Request certificate with Route53:

sudo certbot certonly \
  --dns-route53 \
  -d "*.example.com" \
  -d "example.com" \
  --agree-tos \
  --email [email protected]

Certbot automatically handles DNS record creation and removal through Route53.

Wildcard Certificate Renewal

Certbot automatically renews certificates before expiration through a systemd timer.

Check renewal status:

sudo certbot renew --dry-run

For automated renewal with Cloudflare, ensure credentials are available to root:

sudo cp ~/.cloudflare/cloudflare.ini /root/.cloudflare/
sudo chmod 600 /root/.cloudflare/cloudflare.ini

Create a renewal hook to restart web servers after renewal. Create /etc/letsencrypt/renewal-hooks/post/restart-webserver.sh:

#!/bin/bash
sudo systemctl restart nginx
sudo systemctl restart apache2

Make executable:

sudo chmod +x /etc/letsencrypt/renewal-hooks/post/restart-webserver.sh

Check systemd timer:

sudo systemctl list-timers | grep certbot

Manual renewal:

sudo certbot renew --force-renewal

Renew with Cloudflare credentials:

sudo certbot renew \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.cloudflare/cloudflare.ini

Nginx Configuration

Configure Nginx to use wildcard certificates.

Create Nginx server block at /etc/nginx/sites-available/example.com:

sudo nano /etc/nginx/sites-available/example.com

Add configuration:

server {
    listen 80;
    listen [::]:80;
    server_name example.com *.example.com;
    
    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com *.example.com;
    
    # SSL certificate paths
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    # SSL protocols and ciphers
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # HSTS header
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # Server configuration
    root /var/www/example.com;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Test Nginx configuration:

sudo nginx -t

Restart Nginx:

sudo systemctl restart nginx

Apache Configuration

Configure Apache to use wildcard certificates.

Enable SSL module:

sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod rewrite

Create Apache VirtualHost at /etc/apache2/sites-available/example.com.conf:

sudo nano /etc/apache2/sites-available/example.com.conf

Add configuration:

<VirtualHost *:80>
    ServerName example.com
    ServerAlias *.example.com
    
    # Redirect HTTP to HTTPS
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>

<VirtualHost *:443>
    ServerName example.com
    ServerAlias *.example.com
    
    # SSL certificates
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
    
    # SSL configuration
    SSLProtocol TLSv1.2 TLSv1.3
    SSLCipherSuite HIGH:!aNULL:!MD5
    SSLHonorCipherOrder on
    
    # HSTS header
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    
    # Document root
    DocumentRoot /var/www/example.com
    
    <Directory /var/www/example.com>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Enable the site:

sudo a2ensite example.com.conf

Test Apache configuration:

sudo apache2ctl configtest

Restart Apache:

sudo systemctl restart apache2

Troubleshooting

Check certificate validity:

openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout

Verify certificate covers the domain:

openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text | grep -A5 "Subject Alternative Name"

Test SSL configuration:

# Using SSL Labs
curl https://www.ssllabs.com/api/v3/analyze?host=example.com

# Using OpenSSL
openssl s_client -connect example.com:443 -showcerts

# Using testssl.sh
bash testssl.sh --severity HIGH https://example.com

Debug Certbot issues:

# Enable verbose logging
sudo certbot certonly -v --dns-cloudflare ...

# Check logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log

Verify DNS challenge:

# Check TXT record (during renewal)
dig _acme-challenge.example.com TXT

Conclusion

Wildcard SSL certificates from Let's Encrypt provide a cost-effective way to secure multiple subdomains with a single certificate. This guide covered obtaining wildcard certificates using DNS-based validation with Cloudflare and Route53, automating renewal, and configuring Nginx and Apache. For production deployments, automate renewal through systemd timers, monitor certificate expiration, test SSL configurations regularly with tools like SSL Labs, and establish backup renewal procedures. Wildcard certificates excel for multi-subdomain applications, microservices architectures, and dynamic hosting environments.