Apache HTTP Server remains one of the most widely deployed web servers in the world. Whether you're running a LAMP stack on a VPS, a legacy application behind Apache, or a reverse proxy configuration, SSL certificate expiry is a threat that doesn't care about your server software. An expired cert on your Apache server causes the same result as any other: browser warnings that block users before they see a single pixel of your site.
The problem is that most Apache operators rely on the same manual workflow they've used for years: a calendar reminder, a cron job that might or might not fire, or just hoping someone notices before the cert expires. This article walks through proper Apache SSL certificate monitoring.
The quickest way to check any certificate from the command line:
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -dates
You'll get output like:
notBefore=Sep 15 00:00:00 2026 GMT
notAfter=Dec 14 23:59:59 2026 GMT
If you want the number of days remaining:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null \
| openssl x509 -noout -enddate \
| awk -F'=' '{print $2}' \
| xargs -I{} date -d "{}" +%s \
| xargs -I{} bash -c 'echo $(( ({} - $(date +%s)) / 86400 )) days remaining'
This tells you where you stand right now. It doesn't help when you're on vacation in two weeks and nobody ran the check.
A common Apache misconfiguration: the cert file on disk has been updated but Apache is still serving the old cert from memory. After updating a cert, always restart or gracefully reload Apache:
sudo apachectl graceful
# or
sudo systemctl reload apache2
To confirm which cert Apache is currently serving (vs. which cert file is on disk), use the live TLS handshake check:
openssl s_client -connect localhost:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -subject -dates
This reaches out to Apache directly, even before DNS propagation, which is useful immediately after a cert swap.
Apache configurations frequently host multiple virtual hosts, each with its own SSL certificate. The naive approach — checking the primary domain — misses all of them:
# Apache vhost config (typical)
<VirtualHost *:443>
ServerName api.example.com
SSLCertificateFile /etc/ssl/certs/api_example_com.crt
SSLCertificateKeyFile /etc/ssl/private/api_example_com.key
</VirtualHost>
<VirtualHost *:443>
ServerName admin.example.com
SSLCertificateFile /etc/ssl/certs/admin_example_com.crt
SSLCertificateKeyFile /etc/ssl/private/admin_example_com.key
</VirtualHost>
A script that extracts all SSL cert files and checks each one:
#!/bin/bash
# check-apache-certs.sh — check expiry of all SSL certs in Apache config
WARN_DAYS=30
grep -r "SSLCertificateFile" /etc/apache2/sites-enabled/ \
| awk '{print $3}' | sort -u | while read certfile; do
if [ -f "$certfile" ]; then
expiry=$(openssl x509 -noout -enddate -in "$certfile" 2>/dev/null | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -le "$WARN_DAYS" ]; then
echo "WARNING: $certfile expires in $days_left days ($expiry)"
else
echo "OK: $certfile — $days_left days remaining"
fi
fi
done
Run this script via cron. The challenge is that the alert goes to a log file or email — which means someone has to read it, process it, and act on it.
If you're using Let's Encrypt with Certbot for Apache, the renewal process is more automated, but monitoring still matters:
# Check all Let's Encrypt certs managed by Certbot
sudo certbot certificates
# Dry-run renewal (doesn't actually renew)
sudo certbot renew --dry-run
Let's Encrypt certs expire every 90 days. Certbot's renewal timer fires twice daily, but renewal is only attempted when fewer than 30 days remain. This means:
Checking that Certbot's timer is active isn't the same as confirming the cert was actually renewed:
# Check that the timer is active
systemctl status certbot.timer
# Check when renewal last succeeded
sudo journalctl -u certbot --since "7 days ago" | grep -i "renewed\|error\|failed"
For environments with many virtual hosts or managed servers, a centralized monitoring approach works better than per-server scripts:
#!/bin/bash
# monitor-ssl.sh — check multiple domains and send email if any are expiring
DOMAINS=("example.com" "api.example.com" "admin.example.com" "staging.example.com")
WARN_DAYS=30
ALERT_EMAIL="ops@example.com"
for domain in "${DOMAINS[@]}"; do
expiry_date=$(echo | openssl s_client -connect "$domain:443" \
-servername "$domain" 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
if [ -z "$expiry_date" ]; then
echo "FAILED to check $domain" | mail -s "SSL Check Failed: $domain" "$ALERT_EMAIL"
continue
fi
expiry_epoch=$(date -d "$expiry_date" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -le "$WARN_DAYS" ]; then
echo "$domain SSL expires in $days_left days" \
| mail -s "SSL Expiry Warning: $domain ($days_left days)" "$ALERT_EMAIL"
fi
done
Add to cron:
0 8 * * * /usr/local/bin/monitor-ssl.sh
This approach works but has the same fundamental problem as all shell-script-based monitoring: the script itself can fail silently. If the cron job stops running — server migration, cron daemon issue, disk full preventing log writes — no alerts fire, but you also get no notification that the monitoring stopped.
A complete Apache SSL monitoring setup does the following:
Use the free SSLWatch checker to see how many days remain on any domain's certificate — no signup required, no Apache access needed:
Enter any domain and see days remaining, expiry date, and issuer in seconds. Check all your Apache virtual hosts one by one.
Check SSL expiry → Get expiry alerts →SSLWatch monitors SSL certificate expiry for any domain — including Apache virtual hosts — and sends you email alerts at 30, 14, 7, and 1 day before expiry. It checks from outside your server via a live TLS handshake, so it sees exactly what your visitors see.
No agent to install on your Apache server. No script that can silently fail. No calendar reminders. Add your domains and the alerts arrive on schedule.
SSLWatch monitors all your domains — Apache, Nginx, CDN-terminated, or any HTTPS endpoint — with automatic alerts before they expire.
Join the SSLWatch waitlist → Free expiry checker →