How to Handle SSL Certificate Renewal

Share
How-To Guide

How to Handle SSL Certificate Renewal

Never let your certificates expire unexpectedly

TL;DR

TL;DR (15 minutes):

For Let's Encrypt: Certbot auto-renews via systemd timer or cron - verify with sudo certbot renew --dry-run. For platform-hosted sites (Vercel, Netlify), renewal is automatic. Monitor expiration with free tools like SSL Labs, UptimeRobot, or a simple cron script. Most renewal failures are caused by blocked ports or stopped web servers.

Prerequisites

  • An existing SSL certificate installed on your server or platform
  • For manual servers: SSH access with sudo privileges
  • For Let's Encrypt: Certbot already installed
  • Email address for expiration alerts

Certificate Renewal by Platform

PlatformRenewal MethodYour Action Required
VercelFully automaticNone - handled by platform
NetlifyFully automaticNone - handled by platform
CloudflareFully automaticNone - handled by platform
AWS CloudFront + ACMAutomatic (ACM)None - ACM auto-renews
VPS with CertbotAutomatic (timer/cron)Verify timer is running
Commercial certificateManualRenew before expiration

Check Certificate Expiration Date

1

From command line

# Check expiration for any domain
echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates

# Output shows:
# notBefore=Jan  1 00:00:00 2024 GMT
# notAfter=Apr  1 00:00:00 2024 GMT

# More detailed certificate info:
echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates
2

Using Certbot

# List all certificates and their expiration
sudo certbot certificates

# Output shows:
# Certificate Name: yourdomain.com
#   Domains: yourdomain.com www.yourdomain.com
#   Expiry Date: 2024-04-01 (VALID: 45 days)
3

In browser

Click the padlock icon > "Connection is secure" > "Certificate is valid" to view expiration date.

Set Up Automatic Renewal (Certbot)

1

Verify auto-renewal is configured

# Check if systemd timer exists (most systems)
systemctl list-timers | grep certbot

# Should show something like:
# NEXT                        LEFT          UNIT                  ACTIVATES
# Mon 2024-01-15 03:42:00 UTC 5h left       certbot.timer         certbot.service

# Or check cron
cat /etc/cron.d/certbot
# Should show renewal command running twice daily
2

Test renewal (dry run)

# Always test before relying on auto-renewal
sudo certbot renew --dry-run

# Success output:
# Congratulations, all simulated renewals succeeded:
#   /etc/letsencrypt/live/yourdomain.com/fullchain.pem (success)

# If this fails, fix the issue before your certificate expires
3

Set up systemd timer (if missing)

# Create timer file
sudo nano /etc/systemd/system/certbot.timer

# Add content:
[Unit]
Description=Run certbot twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target
# Create service file
sudo nano /etc/systemd/system/certbot.service

# Add content:
[Unit]
Description=Certbot renewal

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
# Enable and start timer
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# Verify it's running
systemctl status certbot.timer
4

Alternative: Cron job

# Add to crontab
sudo crontab -e

# Add this line (runs at 3:30 AM and 3:30 PM)
30 3,15 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"

# The --deploy-hook runs only if renewal occurred
# Change nginx to apache2 if using Apache

Set Up Renewal Notifications

1

Let's Encrypt email notifications

Let's Encrypt emails you 20 days before expiration if not renewed:

# Update email in Certbot
sudo certbot update_account --email your@email.com

# Verify email is set
sudo certbot show_account
2

Create monitoring script

#!/bin/bash
# save as /usr/local/bin/check-ssl-expiry.sh

DOMAIN="yourdomain.com"
ALERT_DAYS=14
EMAIL="your@email.com"

# Get expiration date
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)

# Convert to epoch
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 "SSL certificate for $DOMAIN expires in $DAYS_LEFT days!" | \
    mail -s "SSL Certificate Expiring: $DOMAIN" $EMAIL
fi
# Make executable and add to cron
chmod +x /usr/local/bin/check-ssl-expiry.sh
sudo crontab -e
# Add: 0 9 * * * /usr/local/bin/check-ssl-expiry.sh
3

Use external monitoring services

  • UptimeRobot: Free SSL monitoring with email/SMS alerts
  • SSL Labs: Bookmark your test URL, check periodically
  • Datadog/PagerDuty: Enterprise monitoring with SSL checks
  • Oh Dear: Affordable monitoring with certificate checks

Security Checklist

Certificate Renewal Security Checklist

  • Auto-renewal configured (systemd timer or cron)
  • Dry-run test passes successfully
  • Deploy hook reloads web server after renewal
  • Email notifications configured for Let's Encrypt
  • External monitoring set up for expiration alerts
  • Port 80 accessible for HTTP-01 challenge (or DNS challenge configured)
  • Logs monitored for renewal failures
  • Backup renewal process documented
  • Multiple domains in certificate are all valid
  • Calendar reminder set for annual review

How to Verify It Worked

1

Check certificate after renewal

# Verify new expiration date
echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates

# Should show dates ~90 days in the future for Let's Encrypt
2

Check Certbot logs

# View recent renewal attempts
sudo tail -100 /var/log/letsencrypt/letsencrypt.log

# Look for:
# "Cert is due for renewal, auto-renewing..."
# "Congratulations, all renewals succeeded"
3

Verify web server reloaded

# Check nginx/apache is serving new certificate
# The serial number should change after renewal
echo | openssl s_client -servername yourdomain.com -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -serial
4

Test site functionality

After renewal, verify your site loads correctly over HTTPS with no certificate warnings.

Manual Renewal (When Needed)

Force immediate renewal

# Force renewal even if not due
sudo certbot renew --force-renewal

# Renew specific certificate
sudo certbot certonly --force-renewal -d yourdomain.com -d www.yourdomain.com

# Reload web server after manual renewal
sudo systemctl reload nginx  # or apache2

Renew with standalone server

# If web server is causing issues, use standalone mode
sudo systemctl stop nginx
sudo certbot renew --standalone
sudo systemctl start nginx

Common Errors and Troubleshooting

Challenge failed: Connection refused on port 80

# Firewall blocking port 80
sudo ufw allow 80/tcp
sudo ufw status

# Or check iptables
sudo iptables -L -n | grep 80

# Some hosts require port 80 open even for HTTPS sites
# Let's Encrypt needs it for HTTP-01 challenge

Could not bind to port 80

# Another service is using port 80
sudo lsof -i :80

# If it's your web server, use webroot or nginx plugin instead of standalone
sudo certbot renew --webroot -w /var/www/html

# Or use the nginx/apache plugin
sudo certbot renew --nginx

DNS problem: NXDOMAIN for domain

# Domain DNS not resolving
dig yourdomain.com A

# Verify DNS points to your server's IP
# If DNS changed recently, wait for propagation

# For DNS-01 challenge (wildcard certs), ensure TXT records are correct

Too many certificates already issued

# Let's Encrypt rate limit: 50 certs per domain per week
# Wait a week, or use staging for testing:
sudo certbot certonly --staging -d yourdomain.com

# Use --expand to add domains to existing certificate instead of new cert
sudo certbot certonly --expand -d yourdomain.com -d newsubdomain.yourdomain.com

Web server not reloading after renewal

# Add deploy hook to reload automatically
sudo certbot renew --deploy-hook "systemctl reload nginx"

# Or edit /etc/letsencrypt/renewal/yourdomain.com.conf:
# Under [renewalparams], add:
# renew_hook = systemctl reload nginx

# Verify hook is set:
cat /etc/letsencrypt/renewal/yourdomain.com.conf | grep hook

Certificate renewed but site shows old certificate

# Web server needs to reload
sudo systemctl reload nginx  # or apache2

# Check nginx is using correct certificate path
grep ssl_certificate /etc/nginx/sites-enabled/*

# Should point to /etc/letsencrypt/live/yourdomain.com/fullchain.pem

# If using a reverse proxy or CDN, their cache may need clearing

Renewal works manually but not automatically

# Check systemd timer is running
systemctl status certbot.timer

# Check for path issues in cron
# Use full paths in cron jobs:
/usr/bin/certbot renew --quiet

# Check cron logs
grep certbot /var/log/syslog

# Ensure certbot user has necessary permissions

Emergency: Certificate Already Expired

If your certificate has already expired, users see security warnings. Act immediately:

# Try immediate renewal
sudo certbot renew --force-renewal

# If that fails, get new certificate
sudo certbot certonly --nginx -d yourdomain.com -d www.yourdomain.com

# Reload web server
sudo systemctl reload nginx

How often do SSL certificates need to be renewed?

Let's Encrypt certificates expire every 90 days, but Certbot attempts renewal when 30 days remain. Commercial certificates typically last 1-2 years. Platform-managed certificates (Vercel, Netlify, Cloudflare) renew automatically with no action required.

What happens if my SSL certificate expires?

Visitors will see a security warning like "Your connection is not private" or "NET::ERR_CERT_DATE_INVALID". Most browsers will block access unless users click through warnings. This causes immediate loss of traffic and severely damages user trust.

Why did my automatic renewal fail?

Common causes include: port 80 blocked by firewall (needed for HTTP-01 challenge), web server stopped or misconfigured, DNS changes that broke domain verification, insufficient disk space, or Certbot/system not running. Check /var/log/letsencrypt/letsencrypt.log for specific errors.

Can I renew my certificate early?

Yes, use sudo certbot renew --force-renewal to renew immediately regardless of expiration date. However, Let's Encrypt has rate limits (50 certificates per domain per week), so don't force-renew unnecessarily.

Do I need to renew platform-managed certificates?

No. Vercel, Netlify, Cloudflare Pages, and similar platforms handle certificate renewal automatically. You don't need to take any action - just ensure your domain remains connected to the platform.

How do I switch from manual to automatic renewal?

If you originally set up certificates manually, you can still enable auto-renewal. Ensure Certbot's systemd timer or cron job is configured, then run sudo certbot renew --dry-run to verify it works. The renewal configuration is stored in /etc/letsencrypt/renewal/.

Run a free security scan to check certificate validity and get expiration alerts.

Start Free Scan
How-To Guides

How to Handle SSL Certificate Renewal