SSL/TLS certificates are the foundation of web security, encrypting data in transit and establishing trust between clients and servers. Yet certificate management remains one of the most neglected areas of IT operations — until a certificate expires and brings down production services. This guide covers automation, monitoring, and best practices for managing certificates at scale.
Understanding Certificate Types
| Type | Validation | Use Case | Cost |
|---|---|---|---|
| Domain Validated (DV) | Domain ownership only | Blogs, small sites, APIs | Free (Let’s Encrypt) to $50/year |
| Organization Validated (OV) | Domain + organization identity | Business websites, intranets | $50-200/year |
| Extended Validation (EV) | Thorough business verification | Banks, e-commerce, government | $200-1000/year |
| Wildcard | All subdomains (*.example.com) | Multi-subdomain environments | $50-500/year |
| SAN/Multi-Domain | Multiple specific domains | Hosting multiple sites on one IP | $100-600/year |
Let’s Encrypt Automation with Certbot
Let’s Encrypt provides free DV certificates with 90-day validity. Certbot automates issuance and renewal.
Initial Setup (Nginx)
# Install Certbot
sudo apt update
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d example.com -d www.example.com
# Certbot automatically:
# 1. Verifies domain ownership via HTTP-01 challenge
# 2. Obtains the certificate from Let's Encrypt
# 3. Configures Nginx SSL directives
# 4. Sets up auto-renewal via systemd timer
Wildcard Certificates (DNS-01 Challenge)
# Wildcard requires DNS-01 validation
sudo certbot certonly --manual --preferred-challenges dns \
-d "*.example.com" -d "example.com"
# For automated DNS validation with Cloudflare:
sudo apt install python3-certbot-dns-cloudflare
# Create Cloudflare credentials file
cat > /etc/letsencrypt/cloudflare.ini << EOF
dns_cloudflare_api_token = your-api-token-here
EOF
chmod 600 /etc/letsencrypt/cloudflare.ini
# Automate wildcard renewal
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "*.example.com" -d "example.com"
Nginx SSL Configuration Best Practices
# /etc/nginx/snippets/ssl-params.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Session caching
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS (strict transport security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Additional security headers
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
Full Nginx Server Block
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/nginx/snippets/ssl-params.conf;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Certificate Monitoring
Automated monitoring prevents surprise expirations:
Shell Script for Expiry Checking
#!/bin/bash
# check-ssl-expiry.sh - Check SSL certificate expiration
DOMAINS="example.com api.example.com shop.example.com"
ALERT_DAYS=14
for domain in $DOMAINS; do
expiry=$(echo | openssl s_client -servername "$domain" \
-connect "$domain:443" 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -lt "$ALERT_DAYS" ]; then
echo "WARNING: $domain expires in $days_left days ($expiry)"
# Send alert via email, Slack, PagerDuty, etc.
else
echo "OK: $domain - $days_left days remaining"
fi
done
Prometheus + Blackbox Exporter
# prometheus.yml
scrape_configs:
- job_name: 'ssl-expiry'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://example.com
- https://api.example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- target_label: __address__
replacement: blackbox-exporter:9115
# Alert rule
groups:
- name: ssl
rules:
- alert: SSLCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 14
labels:
severity: warning
annotations:
summary: "SSL cert for {{ $labels.instance }} expires in less than 14 days"
Renewal Strategies
- Let's Encrypt: Auto-renewal via
certbot renewcron job or systemd timer. Runs twice daily, only renews when within 30 days of expiry. - Commercial certificates: Set calendar reminders 30 days before expiry. Use monitoring tools to send automated alerts.
- Internal/private CAs: Use tools like
step-caorcfsslfor short-lived certificates with automatic rotation.
Certbot Auto-Renewal Verification
# Check renewal timer is active
sudo systemctl status certbot.timer
# Dry run to test renewal
sudo certbot renew --dry-run
# Post-renewal hook to reload Nginx
# /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
Professional SSL Management
At PCCVDI Solutions, we manage SSL/TLS certificates for organizations running hundreds of domains across multiple servers and cloud platforms. From initial certificate procurement to automated monitoring and renewal pipelines, we ensure your certificates never expire unexpectedly. Contact our security team for a free SSL audit of your infrastructure.
