SSL certificate rotation process showing old certificate expiry date, new certificate installation steps and verification across web server and load balancer
# developer tools# website monitoring

How to Rotate SSL Certificates Without Breaking Your Site

Certificate rotation goes wrong in predictable ways: the new certificate is installed but the old one is still being served, the certificate chain is incomplete, or the rotation happens on some servers but not all. The result is either downtime or a security warning that's just as damaging.

Here's a practical guide to rotating SSL certificates correctly, whether you're doing it manually or automating it with Let's Encrypt.


Before You Start

Check What Certificate You're Currently Running

# Check the certificate being served (not just what's on disk)
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates -subject

# Output:
# subject=CN = yourdomain.com
# notBefore=Jan  1 00:00:00 2026 GMT
# notAfter=Apr  1 00:00:00 2026 GMT

The key here is that you're checking what the server is actually serving, not what's on disk. These can differ if nginx/Apache hasn't been reloaded.

Verify Your Certificate Chain

An incomplete chain causes browser warnings even with a valid leaf certificate:

openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -text | grep -A5 "Issuer:"

Your certificate chain should include: your domain certificate → intermediate certificate(s) → root certificate. Most certificate files from commercial CAs (and Let's Encrypt) include the intermediates automatically.


Rotating on Nginx

Step 1: Place New Certificate Files

# Back up existing certificates before making changes
cp /etc/nginx/ssl/yourdomain.crt /etc/nginx/ssl/yourdomain.crt.bak
cp /etc/nginx/ssl/yourdomain.key /etc/nginx/ssl/yourdomain.key.bak

# Copy new certificate files
cp new_certificate.crt /etc/nginx/ssl/yourdomain.crt
cp new_private.key /etc/nginx/ssl/yourdomain.key

Make sure the certificate file includes the full chain (leaf cert + intermediate certs concatenated):

# Concatenate leaf cert and intermediates if separate files
cat yourdomain_cert.crt intermediate_bundle.crt > /etc/nginx/ssl/yourdomain.crt

Step 2: Test Configuration Before Reloading

nginx -t
# Output: nginx: configuration file /etc/nginx/nginx.conf test is successful

Never reload without testing. A configuration error here will take your site down on reload.

Step 3: Reload (Not Restart)

# Reload gracefully — active connections complete, new connections use new config
nginx -s reload
# Or:
systemctl reload nginx

Use reload, not restart. Restart drops active connections; reload completes them gracefully.

Step 4: Verify the New Certificate is Being Served

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -dates

Confirm the notAfter date matches your new certificate's expiry.


Rotating on Apache

# Back up existing certs
cp /etc/apache2/ssl/yourdomain.crt /etc/apache2/ssl/yourdomain.crt.bak
cp /etc/apache2/ssl/yourdomain.key /etc/apache2/ssl/yourdomain.key.bak

# Place new certificate
cp new_certificate.crt /etc/apache2/ssl/yourdomain.crt
cp new_private.key /etc/apache2/ssl/yourdomain.key

# Test configuration
apachectl configtest

# Reload gracefully
systemctl reload apache2

Your Apache SSL configuration should point to the certificate and chain:

SSLCertificateFile /etc/apache2/ssl/yourdomain.crt
SSLCertificateKeyFile /etc/apache2/ssl/yourdomain.key
# For Apache < 2.4.8, use SSLCertificateChainFile for intermediate certs
# For Apache >= 2.4.8, include intermediates in SSLCertificateFile

Rotating Behind a Load Balancer

Load balancers add complexity because certificates may be terminated at the load balancer, at the origin servers, or both.

If SSL is Terminated at the Load Balancer

Most managed load balancers (AWS ALB, GCP Load Balancing, Cloudflare) handle certificates directly. Rotation is done through their dashboards and takes effect without touching your origin servers.

AWS ALB:

# Via AWS CLI — update the certificate on your listener
aws acm import-certificate \
  --certificate fileb://new_cert.pem \
  --private-key fileb://new_key.pem \
  --certificate-chain fileb://chain.pem

# Or use ACM Certificate Manager for auto-renewed certs

Check each origin server is serving the correct certificate if you terminate SSL at the origins too:

for server in 10.0.1.10 10.0.1.11 10.0.1.12; do
    echo -n "$server: "
    echo | openssl s_client -connect $server:443 2>/dev/null \
      | openssl x509 -noout -enddate
done

Rolling Rotation Across Multiple Servers

For zero-downtime rotation across multiple origin servers:

  1. Update one server at a time
  2. After each update, verify the new certificate is being served
  3. Check that the load balancer health checks still pass after reload
  4. Proceed to the next server
# After updating each server
echo | openssl s_client -connect $server:443 2>/dev/null \
  | openssl x509 -noout -enddate

# Verify no connection errors
curl -I --resolve yourdomain.com:443:$server https://yourdomain.com | head -5

Let's Encrypt Auto-Rotation with Certbot

Let's Encrypt certificates expire every 90 days. Certbot handles renewal automatically, but the renewal process can fail silently if not monitored.

Setting Up Auto-Renewal

# Certbot installs a systemd timer or cron job automatically
# Verify it's running:
systemctl status certbot.timer

# Test the renewal process without actually renewing
certbot renew --dry-run

# Force renewal to test the full process
certbot renew --force-renewal

Post-Renewal Hook for Nginx

Certbot renews the certificate files but doesn't automatically reload nginx. Add a post-renewal hook:

# Create /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
systemctl reload nginx

chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Without this hook, Certbot renews the files but nginx continues serving the old certificate from memory until manually reloaded.

Check That Renewal Is Working

# Check when your certificate was last renewed
certbot certificates

# Check systemd timer next scheduled run
systemctl list-timers certbot.timer

The certbot timer runs twice daily. If renewal fails, it retries on subsequent runs — but only for a limited period before the certificate expires.


What Goes Wrong: Common Failure Points

ProblemSymptomFix
Nginx not reloadedBrowser still sees old certsystemctl reload nginx
Incomplete chainBrowser warning about cert chainConcatenate intermediates into cert file
Wrong domain in certBrowser shows cert name mismatchEnsure SAN includes all domains
Post-renewal hook missingCertbot renews but nginx uses old certAdd deploy hook to reload nginx/apache
Load balancer using old certLB terminates TLS with old certUpdate cert at the load balancer
Multiple servers, partial updateInconsistent cert between serversRoll out to all servers, verify each

Monitoring Certificate Expiry

Manual rotation processes fail when someone forgets. Automated rotation processes fail when a hook breaks or a renewal silently errors. Either way, you want an alert before the certificate expires.

Domain Monitor monitors your SSL certificates and alerts you when a certificate is approaching expiry — typically 30 days and 7 days before expiry. This catches both manual renewal failures and broken automated renewal processes before users see a security warning. Create a free account.

See Let's Encrypt renewal failed: common causes and fixes for troubleshooting specifically Let's Encrypt renewal issues, and complete guide to SSL certificates for the broader context on certificate management.


Also in This Series

More posts

Why Your Status Page Matters During an Outage

When your site goes down, your status page becomes the most important page you have. Here's why it matters, what happens when you don't have one, and what a good status page does during a real outage.

Read more
Why Your Domain Points to the Wrong Server

Your domain is resolving, but pointing to the wrong server — showing old content, a previous host's page, or someone else's site entirely. Here's what causes this and how to diagnose it.

Read more
Why Website Monitoring Misses Downtime Sometimes

Uptime monitoring isn't foolproof. Single-location monitors, wrong health check endpoints, long check intervals, and false positives can all cause real downtime to go undetected. Here's what to watch out for.

Read more

Subscribe to our PRO plan.

Looking to monitor your website and domains? Join our platform and start today.