I’ve been on the receiving end of a 2 a.m. page for an expired certificate more than once, and every single time it was preventable. SSL certificate renewal isn’t complicated, but it’s one of those maintenance tasks that’s easy to forget until it becomes an emergency. In this guide, I’ll walk through both manual renewal for purchased certificates and automated renewal using Let’s Encrypt, plus how to make sure Apache is actually serving the new certificate afterward.
Why Certificate Renewal Matters
SSL/TLS certificates have a defined validity period — historically up to a couple of years, but modern CAs (and browser policy) have pushed this down significantly, with many now issuing certificates valid for 90 days to about a year. Once expired, browsers immediately throw hard trust warnings, and most users won’t click past them. Beyond the obvious trust and traffic impact, an expired certificate can also break API integrations, mobile apps, and automated systems that don’t have a “click through the warning” option at all.
Prerequisites
- Access to your certificate provider’s account (or Let’s Encrypt/Certbot if using free automated certificates).
- Root or sudo access to the Apache server.
- Your existing Apache SSL virtual host configuration.
- Your domain’s DNS still pointing correctly to the server (required for most validation methods).
Method 1: Renewing a Commercially Purchased Certificate
Step 1: Generate a New CSR (If Required)
Some CAs let you renew using the original CSR (Certificate Signing Request); others require a new one, especially if your key details or organization info changed. I generate a fresh CSR to be safe:
openssl req -new -newkey rsa:2048 -nodes -keyout yourdomain-new.key -out yourdomain-new.csr
I fill in the prompted fields (Common Name must exactly match your domain, e.g., yourdomain.com).
Step 2: Submit the CSR to Your CA
I log into the CA’s portal (or use their CLI/API if available), submit the CSR, and complete domain validation — usually via email confirmation, a DNS TXT record, or an HTTP file upload, depending on the CA and validation level.
Step 3: Download the Renewed Certificate and Chain
Once issued, I download:
- The new server certificate (
yourdomain.crt) - The intermediate certificate bundle (
intermediate.crtor similar)
Step 4: Combine and Install
Following the same approach I use for chain files:
cat yourdomain.crt intermediate.crt > yourdomain-fullchain.crt
sudo cp yourdomain-fullchain.crt /etc/ssl/certs/
sudo cp yourdomain-new.key /etc/ssl/private/yourdomain.key
sudo chmod 600 /etc/ssl/private/yourdomain.key
Step 5: Update the Apache Configuration (If Filenames Changed)
SSLCertificateFile /etc/ssl/certs/yourdomain-fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/yourdomain.key
Step 6: Test and Reload
sudo apachectl configtest
sudo systemctl reload apache2
I use reload rather than restart to avoid dropping active connections unnecessarily.
Method 2: Renewing a Let’s Encrypt Certificate with Certbot
This is what I use for the vast majority of sites today, mainly because it automates the entire process.
Step 1: Confirm Certbot Is Installed
sudo apt install certbot python3-certbot-apache
On CentOS/RHEL:
sudo yum install certbot python3-certbot-apache
Step 2: Renew Manually (to Test)
sudo certbot renew --dry-run
The --dry-run flag simulates the renewal without actually replacing your live certificate — I always run this first to catch configuration issues before they matter.
Step 3: Perform the Actual Renewal
sudo certbot renew
Certbot checks all certificates it manages and renews any that are within 30 days of expiration (Let’s Encrypt’s recommended renewal window). If none qualify yet, it simply reports that no renewal is due.
Step 4: Automate Renewal
Certbot typically installs a systemd timer or cron job automatically during setup. I verify it exists:
sudo systemctl list-timers | grep certbot
If it’s missing, I add a cron job manually:
sudo crontab -e
0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2"
The --post-hook ensures Apache reloads automatically after a successful renewal — a step I’ve seen people forget when handling renewal manually, resulting in Apache continuing to serve the old (soon-to-expire or already-expired) certificate even after a “successful” renewal.
Step 5: Verify the Renewed Certificate Is Live
Regardless of which method I used, I always verify the actual certificate Apache is serving — not just what’s on disk:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates
I confirm the notAfter date reflects the new expiration, not the old one.
Handling Renewal for Certificates Covering Multiple Domains
If your certificate covers multiple domains (via SAN entries) or a wildcard, renewal works the same way, but I pay extra attention to confirming every covered domain still resolves correctly and passes validation. Certbot, for example, will attempt to validate every domain listed on the certificate during renewal — if even one subdomain’s DNS has changed or a validation file path has moved, the entire renewal can fail, potentially leaving the whole certificate to expire even though most domains were fine.
sudo certbot certificates
I run this periodically to see exactly which domains are covered by which certificate, along with expiration dates, so I’m never surprised by scope I’d forgotten about.
What to Do If You Miss a Renewal Window
If a certificate does expire before renewal, the fix is usually straightforward but urgent: renew immediately using the same process above, and reload Apache the moment the new certificate is in place. I also check whether any monitoring or alerting gaps let the expiration slip through unnoticed, and close that gap so it doesn’t happen again — usually by adding a dedicated uptime monitor that specifically checks certificate expiration dates rather than just HTTP status codes.
Common Mistakes I See
- Renewing the certificate but forgetting to reload Apache — the new cert sits on disk while Apache keeps serving the old, in-memory certificate until reloaded.
- Letting DNS validation lapse — if you’re using DNS-based validation and change DNS providers or records, automated renewal can silently start failing.
- Not testing with
--dry-runbefore relying on unattended automatic renewal. - Manually renewing certificates that Certbot manages, creating conflicting certificate files and confusing configuration paths.
- Ignoring renewal failure notifications — Let’s Encrypt sends expiration warning emails; I make sure these go to an inbox someone actually monitors.
Security Best Practices
- Set your private key permissions correctly every time you regenerate a key:
sudo chmod 600 /etc/ssl/private/yourdomain.key
sudo chown root:root /etc/ssl/private/yourdomain.key
- Prefer generating a new key pair on renewal rather than reusing an old one indefinitely — this limits exposure if an old key was ever compromised without your knowledge.
- Monitor certificate expiration proactively rather than relying solely on renewal automation — automation can fail silently due to DNS changes, expired API credentials, or blocked validation ports.
- Use a monitoring service (or a simple cron-based script with
openssl x509 -checkend) as a safety net:
openssl x509 -checkend 604800 -noout -in /etc/ssl/certs/yourdomain-fullchain.crt
This checks whether the certificate will expire within the next 7 days (604800 seconds) and can be wired into alerting.
Performance Optimization Tips
- Automate renewal fully so certificate changes don’t require manual Apache restarts during business hours — use
reloadin your post-renewal hook, notrestart, to avoid dropping active connections. - Keep certificate validity periods aligned with your renewal automation cadence — shorter-lived certificates (like Let’s Encrypt’s 90-day certs) actually encourage more resilient, automated infrastructure.
- Batch certificate renewals for multiple domains on the same server using Certbot’s built-in support for multiple domains/vhosts rather than running separate manual processes for each.
Troubleshooting
Problem: certbot renew succeeds but the website still shows the old certificate Apache wasn’t reloaded. Run:
sudo systemctl reload apache2
And add a --post-hook to your renewal automation so this never happens again.
Problem: Renewal fails with a domain validation error Check that your domain still resolves correctly and that port 80/443 (depending on validation method) isn’t blocked by a firewall or CDN in front of your server.
dig yourdomain.com
sudo ufw status
Problem: Certbot renewal cron job isn’t running Check the systemd timer or cron job actually exists and has correct permissions:
sudo systemctl status certbot.timer
FAQs
How early should I renew my SSL certificate? For manually managed certificates, I start the process at least 2–4 weeks before expiration to leave room for validation delays. Let’s Encrypt/Certbot automatically targets renewal at the 30-days-remaining mark.
Does renewing a certificate require downtime? No — using systemctl reload apache2 (not restart) applies the new certificate without dropping existing connections.
What happens if my certificate expires before I renew it? Browsers will show hard trust warnings immediately, and most automated clients/APIs will simply fail the connection. There’s no grace period from the browser’s perspective.
Summary and Key Takeaways
Whether you’re renewing a commercially purchased certificate manually or relying on Certbot’s automation for Let’s Encrypt, the same core principle applies: get the new certificate installed correctly, and always reload Apache afterward. Automating this process with a post-renewal hook removes the single most common point of failure — forgetting the reload step.
Key takeaways:
- Always reload (not just replace files) Apache after any certificate renewal.
- Use
certbot renew --dry-runto test automation before trusting it unattended. - Set up expiration monitoring as a safety net independent of your renewal automation.
- Prefer shorter-lived, automated certificates over long-lived, manually managed ones where practical.