← Blog  ·  October 2026

How to Check SSL Certificate Expiry (Online + Command Line)

Every method for checking a certificate's expiry date: browser tools, openssl, curl, Python, Node.js, and CI integration.

SSL certificate expiry is worth checking before it becomes an outage. This guide covers every practical method for checking SSL certificate expiry — from a quick online tool to scriptable approaches you can integrate into CI pipelines.


Fastest Option: Online SSL Certificate Checker

If you just want a quick answer without touching the command line, the free SSL Certificate Checker tool checks any domain and shows you:

Enter a domain name, get the result instantly — no account, no installation, no command line.

Check a Domain’s SSL Certificate Now

Free, instant results — no account required.

Open SSL checker

Method 1: openssl (Most Common Command-Line Approach)

The openssl command-line tool is available on virtually every Unix-like system and is the most widely used method for checking certificate details from the terminal.

Get the expiry date

echo | openssl s_client -connect yourdomain.com:443 \
  -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -dates

Output:

notBefore=Sep 10 12:00:00 2026 GMT
notAfter=Dec  9 11:59:59 2026 GMT

The -servername flag is required for SNI (Server Name Indication) — without it, you may get the wrong certificate on hosts serving multiple domains.

Get days remaining as a number

EXPIRY=$(echo | openssl s_client -connect yourdomain.com:443 \
  -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -enddate \
  | cut -d= -f2)

# Linux (GNU date):
echo $(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 )) days remaining

# macOS (BSD date):
echo $(( ($(date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s) - $(date +%s)) / 86400 )) days remaining

Check the full certificate chain

echo | openssl s_client -connect yourdomain.com:443 \
  -servername yourdomain.com -showcerts 2>/dev/null \
  | openssl x509 -noout -text \
  | grep -E "(Issuer|Subject|Not Before|Not After)"

Check a non-standard port

# SMTP with STARTTLS (port 587)
echo | openssl s_client -connect mail.yourdomain.com:587 \
  -starttls smtp 2>/dev/null | openssl x509 -noout -dates

# LDAPS (port 636)
echo | openssl s_client -connect ldap.yourdomain.com:636 2>/dev/null \
  | openssl x509 -noout -dates
Common gotcha: openssl s_client hangs waiting for stdin if you don't pipe echo into it. Always prefix with echo | or pipe /dev/null in scripts.

Method 2: curl

curl can check certificate validity without extracting dates explicitly. It's useful when you want a quick pass/fail check rather than the exact expiry date:

# Check if the cert is currently valid (exits non-zero if cert error):
curl -s -o /dev/null --max-time 10 https://yourdomain.com
echo $?   # 0 = valid, non-zero = error

For more detail, use --verbose:

curl -v --max-time 10 https://yourdomain.com 2>&1 | grep -E "expire|SSL|TLS|cert"

curl's --cacert flag lets you test against a specific CA bundle, which is useful when validating certificates on internal networks with custom certificate authorities:

curl --cacert /path/to/custom-ca.pem https://internal.yourdomain.com

curl doesn't give you the expiry date in a machine-readable format. For scripting, use openssl. For a quick "is this cert valid right now" check, curl's exit code is the fastest approach.


Method 3: Python

Python's ssl module can retrieve certificate details programmatically. This is the right approach when you need to integrate certificate checking into a larger Python script or application:

import ssl
import socket
from datetime import datetime

def check_ssl_expiry(hostname, port=443):
    context = ssl.create_default_context()
    with socket.create_connection((hostname, port), timeout=10) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert()
    
    expiry_str = cert['notAfter']
    expiry_date = datetime.strptime(expiry_str, '%b %d %H:%M:%S %Y %Z')
    days_remaining = (expiry_date - datetime.utcnow()).days
    
    return {
        'hostname': hostname,
        'expiry_date': expiry_date.strftime('%Y-%m-%d'),
        'days_remaining': days_remaining,
        'issuer': dict(x[0] for x in cert['issuer']),
        'subject': dict(x[0] for x in cert['subject']),
    }

# Usage:
result = check_ssl_expiry('yourdomain.com')
print(f"{result['hostname']}: {result['days_remaining']} days remaining (expires {result['expiry_date']})")

# Check multiple domains:
domains = ['yourdomain.com', 'api.yourdomain.com', 'anotherdomain.com']
for domain in domains:
    try:
        info = check_ssl_expiry(domain)
        status = '⚠️' if info['days_remaining'] < 30 else '✓'
        print(f"{status} {domain}: {info['days_remaining']} days")
    except Exception as e:
        print(f"✗ {domain}: {e}")

For more advanced use cases — including checking the full certificate chain, validating against a custom CA, or extracting Subject Alternative Names — use the cryptography library instead of the stdlib ssl module:

pip install cryptography
import ssl
import socket
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timezone

def get_cert_details(hostname, port=443):
    context = ssl.create_default_context()
    with socket.create_connection((hostname, port), timeout=10) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            der_cert = ssock.getpeercert(binary_form=True)
    
    cert = x509.load_der_x509_certificate(der_cert, default_backend())
    
    san = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
    alt_names = [name.value for name in san.value]
    
    now = datetime.now(timezone.utc)
    days_remaining = (cert.not_valid_after_utc - now).days
    
    return {
        'subject': cert.subject.rfc4514_string(),
        'issuer': cert.issuer.rfc4514_string(),
        'not_before': cert.not_valid_before_utc.isoformat(),
        'not_after': cert.not_valid_after_utc.isoformat(),
        'days_remaining': days_remaining,
        'san': alt_names,
        'serial': hex(cert.serial_number),
    }

Method 4: Node.js

Node.js's tls module provides certificate information via the getPeerCertificate() method:

const tls = require('tls');

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

        if (!cert || !cert.valid_to) {
          return reject(new Error('Could not retrieve certificate'));
        }

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

        resolve({
          hostname,
          subject: cert.subject,
          issuer: cert.issuer,
          validFrom: cert.valid_from,
          validTo: cert.valid_to,
          daysRemaining,
          fingerprint: cert.fingerprint256,
        });
      }
    );

    socket.setTimeout(10000);
    socket.on('timeout', () => reject(new Error('Connection timed out')));
    socket.on('error', reject);
  });
}

// Usage:
checkSSLExpiry('yourdomain.com')
  .then(info => {
    console.log(`${info.hostname}: ${info.daysRemaining} days remaining`);
    if (info.daysRemaining < 30) {
      console.warn('⚠️  Certificate expiring soon!');
    }
  })
  .catch(err => console.error(`Error: ${err.message}`));

// Check multiple domains:
const domains = ['yourdomain.com', 'api.yourdomain.com'];
Promise.allSettled(domains.map(checkSSLExpiry)).then(results => {
  results.forEach((result, i) => {
    if (result.status === 'fulfilled') {
      const { hostname, daysRemaining } = result.value;
      console.log(`${daysRemaining >= 30 ? '✓' : '⚠️'} ${hostname}: ${daysRemaining} days`);
    } else {
      console.log(`✗ ${domains[i]}: ${result.reason.message}`);
    }
  });
});

Method 5: CI Pipeline Integration

Adding a certificate check to your deployment pipeline catches expiry issues at deploy time, surfacing them in your CI logs where engineers are already looking:

#!/bin/bash
# Add to your post-deploy CI step
DOMAIN="yourdomain.com"
WARN_DAYS=30

EXPIRY=$(echo | openssl s_client -connect "$DOMAIN":443 \
  -servername "$DOMAIN" 2>/dev/null \
  | openssl x509 -noout -enddate \
  | cut -d= -f2)

DAYS=$(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 ))

echo "SSL cert for $DOMAIN: $DAYS days remaining (expires $EXPIRY)"

if [ "$DAYS" -lt "$WARN_DAYS" ]; then
  echo "::warning::SSL certificate expires in $DAYS days — renewal required"
fi

if [ "$DAYS" -lt 0 ]; then
  echo "::error::SSL certificate is EXPIRED"
  exit 1
fi

GitHub Actions workflow step:

- name: Check SSL certificate
  run: |
    DOMAIN="${{ env.PRODUCTION_DOMAIN }}"
    EXPIRY=$(echo | openssl s_client -connect "$DOMAIN":443 \
      -servername "$DOMAIN" 2>/dev/null \
      | openssl x509 -noout -enddate | cut -d= -f2)
    DAYS=$(( ($(date -d "$EXPIRY" +%s) - $(date +%s)) / 86400 ))
    echo "Certificate expires in $DAYS days"
    if [ "$DAYS" -lt 30 ]; then
      echo "::warning::SSL cert for $DOMAIN expires in $DAYS days"
    fi

This only runs on deploy, so it won't catch a cert that expires between deploys — it complements rather than replaces dedicated monitoring.


Quick Reference: Which Method to Use

Situation Best method
Quick manual check, no terminal SSL Certificate Checker online tool
Terminal, one-off check openssl s_client
Quick pass/fail validation curl -s -o /dev/null
Python application integration ssl stdlib or cryptography library
Node.js application integration tls.connect() + getPeerCertificate()
CI/CD pipeline check openssl in a shell step
Shell script monitoring loop openssl + cron job

Why Manual Checking Doesn’t Scale

Every method above requires you to actively run a check. That's fine for a one-time inspection or integrating into a script you already run — but it's not a substitute for proactive monitoring.

The problem with manual checking:

The right workflow is: use manual checking (online tool or openssl) to audit your current state and troubleshoot specific certs, and use automated monitoring to get proactive expiry alerts across all your domains.

From Manual Checking to Automated Alerts

Check your certificates right now with the free SSL checker — then set up SSLWatch for automated 60/30/7-day expiry alerts so you never have to remember to check again.

Free SSL checker SSLWatch early access