← Blog  ·  October 2026

How to Monitor SSL Certificate Expiry in Node.js

Use the built-in tls module to check expiry dates programmatically — no external dependencies required.

SSL certificates expire silently. There's no runtime error, no log entry, no notification from your hosting provider. One morning your site returns a browser security warning, your API clients start throwing UNABLE_TO_VERIFY_LEAF_SIGNATURE, and users start filing support tickets. Node.js gives you everything you need to catch this before it happens — if you wire it up yourself.


Check SSL certificate expiry with Node.js tls module

The tls module is part of Node.js core. No npm install required. Here's the minimal pattern for fetching a certificate's expiry date:

const tls = require('tls');

function checkSSLExpiry(hostname) {
  return new Promise((resolve, reject) => {
    const socket = tls.connect(
      443,
      hostname,
      { servername: hostname, rejectUnauthorized: false },
      () => {
        const cert = socket.getPeerCertificate();
        socket.destroy();

        if (!cert || !cert.valid_to) {
          return reject(new Error(`No certificate returned for ${hostname}`));
        }

        const expiresAt = new Date(cert.valid_to);
        const daysRemaining = Math.floor(
          (expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
        );

        resolve({
          hostname,
          expiresAt: expiresAt.toISOString(),
          daysRemaining,
          issuer: cert.issuer?.O ?? 'unknown',
          subject: cert.subject?.CN ?? hostname,
        });
      }
    );

    socket.setTimeout(5000, () => {
      socket.destroy();
      reject(new Error(`Timeout connecting to ${hostname}`));
    });

    socket.on('error', reject);
  });
}

// Example usage
checkSSLExpiry('github.com').then(console.log).catch(console.error);

Sample output:

{
  hostname: 'github.com',
  expiresAt: '2026-11-29T23:59:59.000Z',
  daysRemaining: 55,
  issuer: 'Sectigo Limited',
  subject: 'github.com'
}
⚠️ rejectUnauthorized: false Setting this to false lets you inspect expiring certificates even when the chain is already broken. For production HTTPS requests you still want true. This flag only applies to the tls.connect call used to read the certificate — it doesn't affect how your application handles TLS elsewhere.

Check multiple domains in parallel

Real applications expose dozens of domains — apex domains, subdomains, API endpoints, CDN origins. Check them all in one pass:

const domains = [
  'example.com',
  'api.example.com',
  'staging.example.com',
  'www.example.com',
];

async function checkAllDomains(hostnames) {
  const results = await Promise.allSettled(
    hostnames.map(h => checkSSLExpiry(h))
  );

  return results.map((r, i) => ({
    hostname: hostnames[i],
    status: r.status,
    ...(r.status === 'fulfilled' ? r.value : { error: r.reason.message }),
  }));
}

checkAllDomains(domains).then(results => {
  results.forEach(r => {
    if (r.status === 'rejected') {
      console.error(`❌  ${r.hostname}: ${r.error}`);
    } else if (r.daysRemaining <= 14) {
      console.warn(`⚠️  ${r.hostname}: expires in ${r.daysRemaining} days`);
    } else {
      console.log(`✅  ${r.hostname}: ${r.daysRemaining} days remaining`);
    }
  });
});

Promise.allSettled (rather than Promise.all) ensures one unreachable domain doesn't abort checks for all the others.


Send email alerts when expiry is near

Checking is only half the job. You also need to be notified. Add Nodemailer to fire an alert when any domain is within 30 days of expiry:

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: 465,
  secure: true,
  auth: {
    user: process.env.SMTP_USER,
    pass: process.env.SMTP_PASS,
  },
});

async function sendExpiryAlert(result) {
  await transporter.sendMail({
    from: '"SSL Monitor" <alerts@example.com>',
    to: process.env.ALERT_EMAIL,
    subject: `⚠️ SSL expiry warning: ${result.hostname} expires in ${result.daysRemaining} days`,
    text: [
      `Domain: ${result.hostname}`,
      `Expires: ${result.expiresAt}`,
      `Days remaining: ${result.daysRemaining}`,
      `Issuer: ${result.issuer}`,
      '',
      'Renew your certificate before it expires to avoid downtime.',
    ].join('\n'),
  });
}

async function monitorAndAlert(hostnames, thresholdDays = 30) {
  const results = await checkAllDomains(hostnames);

  for (const r of results) {
    if (r.status === 'fulfilled' && r.daysRemaining <= thresholdDays) {
      console.warn(`Sending alert for ${r.hostname} (${r.daysRemaining} days remaining)`);
      await sendExpiryAlert(r);
    }
  }
}

Run the check on a schedule with node-cron

Wire it to a daily schedule so expiry alerts fire automatically:

const cron = require('node-cron');

// Run every day at 8am
cron.schedule('0 8 * * *', async () => {
  console.log(`[${new Date().toISOString()}] Running SSL expiry check`);
  await monitorAndAlert([
    'example.com',
    'api.example.com',
  ], 30);
});

Install the single dependency:

npm install node-cron

Nodemailer is optional — swap in any transactional email library (SendGrid, Resend, Postmark) that your stack already uses.


Where the self-hosted approach gets painful

This setup works. But as soon as you operate it for more than a few domains or a few weeks, friction compounds:

The core problem A self-hosted SSL checker solves the technical check. It doesn't solve the reliability problem: who alerts you when the alerter breaks?

Check any domain right now — free tool

Before building your own monitor, check your most important domains with the free SSL checker below. It calls a real TLS handshake and returns days remaining, issuer, and expiry date — no signup required.

🔒 Free SSL Certificate Checker

Enter any domain to instantly see days remaining, expiry date, and issuer. No signup required.

Check a domain now →

If you want automated alerts at 30, 14, 7, and 1 day before expiry — without running your own Node.js process — SSLWatch monitors your domains and emails you before they expire.

📬 SSLWatch — Automated SSL Monitoring

Add your domains once. Get email alerts at 30, 14, 7, and 1 day before each certificate expires. No infrastructure to maintain.

Get early access → Free checker →