# SSL certificate expiry alerts by email

> SSLMonitor emails one report per run when any certificate has fewer than 30 days left, fewer than 7 days left, or cannot be read. This page explains the rules, the email and the two delivery methods.

Source: https://sslmonitor.com/ssl-certificate-expiry-alerts/

## When does SSLMonitor send an alert?

SSLMonitor sends one email per run when at least one domain is not `OK`. It sends nothing when every certificate has more days left than `WARNING_DAYS`. Each domain gets one of four states:

| State | Rule (default values) | Row color in the email |
| --- | --- | --- |
| `OK` | 30 or more whole days left | White |
| `WARNING` | Fewer than `WARNING_DAYS` (30) days left | Light yellow |
| `CRITICAL` | Fewer than `CRITICAL_DAYS` (7) days left, or already expired | Light red |
| `ERROR` | No certificate could be read (DNS failure, port 443 closed, no TLS) | Light red |

Days left are whole days, rounded down: a certificate that expires in 9 days and 20 hours shows `9`. An expired certificate shows a negative number and counts as `CRITICAL`.

Because cron runs the script every day, a domain that stays in `WARNING` sends a report every day until the certificate is renewed. There is no separate "all clear" email: the reports stop once every domain is `OK` again.

## What is in the alert email?

The subject line is `SSL Certificate Alert: <server hostname> - <YYYY-MM-DD>`. The body is an HTML report with the run date, the server hostname and a table of every domain in your list, including the ones that are `OK`. With the API method the script also sends a plain text copy of the same report.

A report for three domains looks like this (sample data):

| Domain | Expiry Date | Days Left | Status |
| --- | --- | --- | --- |
| example.com | Jan 14 23:59:59 2027 GMT | 87 | OK |
| api.example.com | Nov 12 08:30:00 2026 GMT | 24 | WARNING |
| old.example.net |  | 0 | ERROR |

Below the table the email reminds you to renew at least 14 days before expiry and gives two renewal hints: run `certbot renew` for Let's Encrypt certificates, or contact your certificate provider for others.

## How are alerts sent?

The script has two delivery functions. Only one is active at a time; the Forward Email API is the default.

### Forward Email API (default)

`send_email_api` posts the report to `https://api.forwardemail.net/v1/emails` with `curl`. It authenticates with HTTP Basic auth, using your API token as the username and an empty password, which is the method the [Email API docs](https://forwardemail.net/en/email-api) describe. Every field is form-encoded, so the multi-line HTML report arrives intact.

What you need:

- A Forward Email account on a paid plan. The [Forward Email FAQ](https://forwardemail.net/en/faq) states that outbound SMTP comes with all paid plans.
- A domain in that account with DKIM, Return-Path and DMARC set up, and an alias on it to use as `EMAIL_FROM`.
- Your API token from the [Security page](https://forwardemail.net/en/my-account/security) of your account, set as `FORWARD_EMAIL_API_KEY`.

The script logs `Alert email sent via API` when the API accepts the message and `Failed to send alert email via API` when it rejects it or cannot be reached.

### Local sendmail

`send_email_smtp` pipes the report, with `Subject`, `From`, `To` and `Content-Type: text/html` headers, to `sendmail -t`. It works with any mail transfer agent that provides a `sendmail` command, such as Postfix, Exim or msmtp. To deliver through Forward Email, configure that agent to relay through `smtp.forwardemail.net` on port 465 (SSL/TLS) or 587 (STARTTLS), logging in with the full alias address and its generated password.

This method logs `Alert email sent via SMTP` after handing the message to `sendmail`. It does not check whether `sendmail` succeeded, so check your mail log if a report does not arrive.

## Can I send alerts to more than one person?

Yes. Put several addresses in `EMAIL_TO`, separated by commas: `EMAIL_TO="ops@example.com,admin@example.com"`. The Forward Email API accepts a comma-separated `to` list, and `sendmail -t` reads every address in the `To:` header. You can also point `EMAIL_TO` at one Forward Email alias that forwards to the whole team.

## How do I change when alerts start?

Edit `WARNING_DAYS` and `CRITICAL_DAYS` at the top of the script. Set `WARNING_DAYS` below the number of days left when your ACME client normally renews, so a routine renewal does not send a warning. [Certificate lifetimes](/certificate-lifetimes/#what-thresholds-should-you-use) has suggested values for 90-day, 45-day and longer certificates.

## Why not rely on my certificate authority's reminders?

Not every certificate authority sends them, and they only cover certificates that authority issued. Let's Encrypt [ended its expiration reminder emails on June 4, 2025](https://letsencrypt.org/2025/06/26/expiration-notification-service-has-ended) and points its users to third-party monitoring services instead. A reminder from the authority also says nothing about which certificate your server is actually serving. SSLMonitor checks what each server returns, so it also catches a renewed certificate that was never installed or a web server that was never reloaded.
