← Blog  ·  October 2026

Apache SSL Certificate Monitoring: Automated Expiry Alerts for Your Web Server

Your Apache cert is valid today. In 30 days it won't be. Here's how to know before that happens.

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.


Check Apache SSL certificate expiry manually

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.


Check the certificate Apache is actually serving

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 virtual host certificate monitoring

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.


Let's Encrypt with Certbot and Apache

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:

Let's Encrypt renewal failure scenarios Certbot renewal fails silently when: the renewal hook script has a permissions error, the Apache validation challenge fails (common after firewall or port changes), the ACME endpoint is unreachable, or systemd's certbot.timer is accidentally disabled after a system update.

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"

Multi-domain Apache SSL monitoring with a cron script

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.


What Apache SSL monitoring should actually look like

A complete Apache SSL monitoring setup does the following:

The critical gap: on-server vs. external monitoring Your Apache cron script reads the cert file directly — it checks what's on disk, not what Apache is actively serving. An external TLS handshake check verifies what visitors actually encounter. These are different things, especially after a cert renewal where Apache wasn't reloaded.

Check your Apache SSL certs now

Use the free SSLWatch checker to see how many days remain on any domain's certificate — no signup required, no Apache access needed:

Free SSL certificate expiry checker

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 →

Automated Apache SSL monitoring: the full solution

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.

Never miss an Apache SSL expiry again

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 →