Many sysadmins using Let's Encrypt certificates with Apache HTTPD encounter this scenario: you've successfully renewed certificates using certbot renew --webroot
, but Apache continues serving the old certificates. This happens because Apache caches SSL certificates in memory and doesn't automatically detect filesystem changes.
Instead of a full restart, Apache provides a graceful reload mechanism that:
- Maintains existing connections
- Applies configuration changes
- Loads renewed certificates
# The proper way to reload certificates
sudo systemctl reload apache2 # For systemd systems
# OR
sudo service apache2 reload # For SysVinit systems
# OR directly using apachectl
sudo apachectl graceful
For seamless renewals, add this to your Certbot renewal hook:
# /etc/letsencrypt/renewal-hooks/post/reload-apache.sh
#!/bin/bash
systemctl reload apache2 || apachectl graceful
Make it executable:
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-apache.sh
After renewal and reload, verify with:
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com | openssl x509 -noout -dates
Compare with your certificate files:
openssl x509 -noout -dates -in /etc/letsencrypt/live/yourdomain.com/cert.pem
For special cases, you can configure Apache to check certificates more frequently:
# In your SSL VirtualHost configuration
SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLUseStapling on
# Refresh certificates every 6 hours (21600 seconds)
SSLStaplingStandardCacheTimeout 21600
Many sysadmins using Certbot with the --webroot
plugin encounter this scenario:
# Certbot reports success
sudo certbot renew --webroot -w /var/www/html/
# But Apache keeps serving old certificate
openssl s_client -connect example.com:443 | openssl x509 -noout -dates
Apache doesn't automatically reload SSL certificates after renewal. The certificates are cached in memory until one of these occurs:
- Full service restart (
systemctl restart httpd
) - Graceful reload (
systemctl reload httpd
) - Sending the HUP signal (
kill -HUP $(pidof httpd)
)
Here's the proper way to handle certificate renewal without service interruption:
# 1. Renew certificates
sudo certbot renew --webroot -w /var/www/html/ --post-hook "systemctl reload apache2"
# Alternative method using hook scripts
sudo certbot renew --webroot -w /var/www/html/ --deploy-hook "/etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh"
Create /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh
:
#!/bin/sh
# Check if renewal actually occurred
if [ -n "$RENEWED_DOMAINS" ]; then
# Reload Apache gracefully
systemctl reload apache2 || systemctl reload httpd
logger "Reloaded Apache after Let's Encrypt renewal"
fi
After renewal and reload, verify with:
# Check certificate dates
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates
# Check Apache's loaded certificates
sudo apachectl -t -D DUMP_CERTS | grep -A10 "example.com"
- Ensure the web server user (www-data/apache) has read access to
/etc/letsencrypt/live/
- Check SELinux contexts if using RHEL/CentOS:
restorecon -Rv /etc/letsencrypt/
- Verify symlinks in
/etc/letsencrypt/live/
point to updated certificates
For cron-based renewal, use this in /etc/cron.d/certbot
:
0 0,12 * * * root /usr/bin/certbot renew --quiet --post-hook "systemctl reload httpd"
Consider monitoring with:
- Certificate expiration dates (Nagios/Icinga plugins)
- Apache error logs for SSL-related issues
- Certbot's renewal log at
/var/log/letsencrypt/letsencrypt.log