• Home
  • Guides
    • All
    • Linux
    • Programming
    • Tools
    • WordPress
    Monitor SSL Expiration with Python

    Monitor SSL Expiration with Python

    Building a Simple WordPress Post List Tool with PHP

    Building a Simple WordPress Post List Tool with PHP

    Monitoring Web Page Changes with Python

    Monitoring Web Page Changes with Python

    My SSH Setup: How I Manage Multiple Servers

    My SSH Setup: How I Manage Multiple Servers

    Building a Network Tracker Auditor for Privacy with Python

    Building a Network Tracker Auditor for Privacy with Python

    Streaming Audio Files Securely with PHP

    Streaming Audio Files Securely with PHP

    Scraping Web Data with Python Helium

    Scraping Web Data with Python Helium

    Building a Secure 2FA Authenticator with Python

    Building a Secure 2FA Authenticator with Python

    Building a Cache Warmer with Python

    Building a Cache Warmer with Python

  • Blog
    • All
    • Artificial Intelligence
    • Privacy
    • Reviews
    • Security
    • Tutorials
    Why Stable Websites Outperform Flashy Redesigns

    Why Stable Websites Outperform Flashy Redesigns

    AdGuard Ad Blocker Review

    AdGuard Ad Blocker Review

    Surfshark VPN Review

    Surfshark VPN Review

    Nmap Unleash the Power of Cybersecurity Scanning

    Nmap: Unleash the Power of Cybersecurity Scanning

    Floorp Browser Review

    Floorp Browser Review

    Understanding Man-in-the-Middle Attacks

    Understanding Man-in-the-Middle Attacks

    Privacy-Focused Analytics

    Privacy-Focused Analytics: Balancing Insights and Integrity

    Safeguarding Your Facebook Account

    Safeguarding Your Facebook Account: Understanding the Differences Between Hacking and Cloning

    38 essential points to harden WordPress

    38 Essential Points to Harden WordPress

  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attacks
  • Contact
    • General Contact
    • Website Administration & Cybersecurity
No Result
View All Result
  • Home
  • Guides
    • All
    • Linux
    • Programming
    • Tools
    • WordPress
    Monitor SSL Expiration with Python

    Monitor SSL Expiration with Python

    Building a Simple WordPress Post List Tool with PHP

    Building a Simple WordPress Post List Tool with PHP

    Monitoring Web Page Changes with Python

    Monitoring Web Page Changes with Python

    My SSH Setup: How I Manage Multiple Servers

    My SSH Setup: How I Manage Multiple Servers

    Building a Network Tracker Auditor for Privacy with Python

    Building a Network Tracker Auditor for Privacy with Python

    Streaming Audio Files Securely with PHP

    Streaming Audio Files Securely with PHP

    Scraping Web Data with Python Helium

    Scraping Web Data with Python Helium

    Building a Secure 2FA Authenticator with Python

    Building a Secure 2FA Authenticator with Python

    Building a Cache Warmer with Python

    Building a Cache Warmer with Python

  • Blog
    • All
    • Artificial Intelligence
    • Privacy
    • Reviews
    • Security
    • Tutorials
    Why Stable Websites Outperform Flashy Redesigns

    Why Stable Websites Outperform Flashy Redesigns

    AdGuard Ad Blocker Review

    AdGuard Ad Blocker Review

    Surfshark VPN Review

    Surfshark VPN Review

    Nmap Unleash the Power of Cybersecurity Scanning

    Nmap: Unleash the Power of Cybersecurity Scanning

    Floorp Browser Review

    Floorp Browser Review

    Understanding Man-in-the-Middle Attacks

    Understanding Man-in-the-Middle Attacks

    Privacy-Focused Analytics

    Privacy-Focused Analytics: Balancing Insights and Integrity

    Safeguarding Your Facebook Account

    Safeguarding Your Facebook Account: Understanding the Differences Between Hacking and Cloning

    38 essential points to harden WordPress

    38 Essential Points to Harden WordPress

  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attacks
  • Contact
    • General Contact
    • Website Administration & Cybersecurity
No Result
View All Result
Home Guides Programming Python

Monitor SSL Expiration with Python

Jonathan Moore by Jonathan Moore
11 hours ago
Reading Time: 6 mins read
A A
Monitor SSL Expiration with Python
FacebookTwitter

SSL certificates are one of those systems that work quietly in the background until something goes wrong. When a certificate expires, browsers immediately display warnings that can discourage visitors from continuing. Even if your server is running perfectly, an expired certificate creates the impression that your site is unsafe or broken. Preventing that situation requires visibility into certificate expiration before it becomes a problem.

Most of my servers use Let’s Encrypt with automatic renewal, which works reliably most of the time. However, renewal still depends on DNS configuration, firewall access, server availability, and correct certificate validation. If one of those pieces fails, renewal may not occur even though everything appears normal. Rather than relying entirely on automation, I prefer to actively monitor expiration dates.

This Python script connects to each domain, retrieves its SSL certificate, and reports how many days remain before expiration. This provides early warning if a certificate is approaching expiration or if something is misconfigured. It allows problems to be addressed proactively instead of reactively.

The Complete Script

Save this file as: check_ssl_expiration.py

#!/usr/bin/env python3

# Import modules required for SSL connections, networking,
# command-line arguments, and working with dates and time.
import ssl
import socket
import sys
from datetime import datetime, timezone

# Default HTTPS port used when none is specified.
DEFAULT_PORT = 443

# Number of days before expiration to trigger a warning.
DEFAULT_WARN_DAYS = 30

# Timeout in seconds when attempting to connect to a server.
DEFAULT_TIMEOUT = 5


def load_domains(filename):
    """
    Load domain names from a text file.

    Each line should contain either:
    - domain.com
    - domain.com:port

    Blank lines and lines starting with '#' are ignored.
    """

    domains = []

    try:
        # Open the domain list file for reading.
        with open(filename, "r") as file:

            # Read each line from the file.
            for line in file:

                # Remove whitespace and newline characters.
                line = line.strip()

                # Skip empty lines.
                if not line:
                    continue

                # Skip comment lines.
                if line.startswith("#"):
                    continue

                # Check if a custom port is specified.
                if ":" in line:

                    # Split domain and port.
                    host, port = line.split(":", 1)

                    # Add domain and port to the list.
                    domains.append(
                        (host.strip(), int(port.strip()))
                    )

                else:
                    # Use default HTTPS port.
                    domains.append(
                        (line, DEFAULT_PORT)
                    )

    except FileNotFoundError:

        # Exit if the domain file does not exist.
        print(f"Domain file not found: {filename}")
        sys.exit(1)

    return domains


def get_certificate_expiration(host, port):
    """
    Connect to the server and retrieve the SSL certificate
    expiration date.
    """

    # Create a secure SSL context using system defaults.
    context = ssl.create_default_context()

    # Establish a TCP connection to the server.
    with socket.create_connection(
        (host, port),
        timeout=DEFAULT_TIMEOUT
    ) as sock:

        # Wrap the socket in SSL to perform the TLS handshake.
        with context.wrap_socket(
            sock,
            server_hostname=host
        ) as secure_sock:

            # Retrieve certificate information.
            cert = secure_sock.getpeercert()

    # Extract expiration date from certificate.
    expiration_str = cert["notAfter"]

    # Convert expiration string into a datetime object.
    expiration_date = datetime.strptime(
        expiration_str,
        "%b %d %H:%M:%S %Y %Z"
    )

    # Assign UTC timezone to expiration date.
    expiration_date = expiration_date.replace(
        tzinfo=timezone.utc
    )

    return expiration_date


def get_days_remaining(expiration_date):
    """
    Calculate the number of days remaining until expiration.
    """

    # Get current UTC time.
    now = datetime.now(timezone.utc)

    # Calculate time difference.
    remaining = expiration_date - now

    # Convert seconds to whole days.
    return int(remaining.total_seconds() / 86400)


def main():
    """
    Main program execution.
    Loads domains, checks expiration dates,
    and displays results.
    """

    # Default domain file.
    domain_file = "domains.txt"

    # Default warning threshold.
    warn_days = DEFAULT_WARN_DAYS

    # Allow domain file to be specified via command-line.
    if len(sys.argv) >= 2:
        domain_file = sys.argv[1]

    # Allow warning threshold to be specified.
    if len(sys.argv) >= 3:
        warn_days = int(sys.argv[2])

    # Load domain list.
    domains = load_domains(domain_file)

    print(
        f"Checking SSL expiration for {len(domains)} domain(s)"
    )
    print()

    # Check each domain individually.
    for host, port in domains:

        # Format display label.
        label = (
            host if port == 443 else f"{host}:{port}"
        )

        try:

            # Retrieve certificate expiration date.
            expiration_date = get_certificate_expiration(
                host,
                port
            )

            # Calculate days remaining.
            days_remaining = get_days_remaining(
                expiration_date
            )

            # Format expiration date for display.
            expiration_str = expiration_date.strftime(
                "%Y-%m-%d %H:%M:%S UTC"
            )

            # Display warning if expiration is near.
            if days_remaining <= warn_days:

                print(f"[WARN]  {label}")
                print(
                    f"        Expires: {expiration_str}"
                )
                print(
                    f"        Days remaining: {days_remaining}"
                )
                print()

            else:

                # Display normal status.
                print(f"[OK]    {label}")
                print(
                    f"        Expires: {expiration_str}"
                )
                print(
                    f"        Days remaining: {days_remaining}"
                )
                print()

        except Exception as e:

            # Handle connection or certificate errors.
            print(f"[ERROR] {label}")
            print(f"        {e}")
            print()


# Run main function when script is executed directly.
if __name__ == "__main__":
    main()

Creating the Domains File

This script reads domains from a simple text file in the same folder as the script. Each domain goes on its own line, which keeps the script clean and makes it easy to add or remove sites without editing Python code. I also like this approach because I can reuse the same script on different servers by swapping out the domain list. The script ignores blank lines and any line that starts with #, so you can leave notes in the file without breaking anything.

Create a file named domains.txt and add your domains like this:

google.com
godaddy.com
netflix.com
amazon.com

If you need to check a service running on a non-standard port, you can add :port after the hostname. This is useful for internal dashboards, alternate HTTPS ports, or staging environments that are not on 443. Here’s an example:

example.com:8443
staging.example.com:9443

If you want to keep notes in the file, add them as comments:

# Production
google.com
amazon.com

# Internal tools
example.com:8443

Example Output

When the script runs, it connects to each domain and prints the certificate status. This output shows whether the certificate is safe, approaching expiration, or failed to retrieve.

Example:

Checking SSL expiration for 4 domain(s)

[OK]    google.com
        Expires: 2026-04-20 08:39:19 UTC
        Days remaining: 65

[OK]    godaddy.com
        Expires: 2027-02-13 23:13:55 UTC
        Days remaining: 365

[OK]    netflix.com
        Expires: 2026-09-04 09:49:22 UTC
        Days remaining: 203

[OK]    amazon.com
        Expires: 2027-01-23 23:59:59 UTC
        Days remaining: 344

This format makes it easy to quickly identify domains that require attention. The warning label provides early notice, while error messages indicate connection or configuration problems. This immediate visibility helps prevent unexpected certificate expiration.

Importing Required Modules

The script begins by importing several built-in Python modules that provide the necessary functionality. These modules allow the script to establish secure connections, communicate over the network, and perform time calculations.

import ssl
import socket
import sys
from datetime import datetime, timezone

The ssl module handles encrypted TLS connections and allows the script to retrieve certificate information. The socket module creates the underlying network connection that SSL operates on top of. The sys module allows command-line arguments to be passed to the script, which makes it easier to automate and customize. The datetime module allows the script to convert expiration dates into usable values and calculate how much time remains.

Together, these modules provide everything needed without requiring external dependencies.

Defining Configuration Values

The script defines default values that control how it behaves.

DEFAULT_PORT = 443
DEFAULT_WARN_DAYS = 30
DEFAULT_TIMEOUT = 5

Port 443 is the standard HTTPS port, so it is used by default when connecting to domains. The warning threshold is set to 30 days, which provides enough time to correct renewal problems before expiration occurs. The timeout prevents the script from hanging indefinitely if a server does not respond.

Using constants makes the script easier to maintain. These values can be changed without modifying the core logic.

Loading the Domain List

The load_domains function reads the domain list from a file.

def load_domains(filename):
    domains = []

The function opens the file and reads each line individually. It ignores empty lines and comment lines to keep the input clean and readable.

with open(filename, "r") as file:
    for line in file:
        line = line.strip()

Each domain is added to a list, along with its port number if specified.

domains.append((line, DEFAULT_PORT))

This separation between input and logic makes the script easier to maintain and expand.

Retrieving the Certificate

The script retrieves certificate information by connecting securely to the server.

context = ssl.create_default_context()

with socket.create_connection((host, port), timeout=DEFAULT_TIMEOUT) as sock:
    with context.wrap_socket(sock, server_hostname=host) as secure_sock:
        cert = secure_sock.getpeercert()

This process establishes a secure connection and performs a TLS handshake. During this handshake, the server provides its certificate. The script retrieves the certificate and extracts the expiration date.

This approach ensures the script retrieves the actual certificate presented to visitors.

Converting and Calculating Expiration Time

The expiration date must be converted into a usable format before calculations can be performed.

expiration_date = datetime.strptime(
    expiration_str,
    "%b %d %H:%M:%S %Y %Z"
)

Once converted, the script calculates how much time remains.

remaining = expiration_date - now

This allows the script to determine whether the certificate is approaching expiration.

Processing and Displaying Results

The main loop processes each domain individually.

for host, port in domains:

The script calculates remaining time and displays the results clearly.

print(f"[OK] {label}")

Error handling ensures the script continues even if a domain fails.

Final Thoughts

Certificate expiration is predictable, but it is easy to overlook without monitoring. This script provides a simple and reliable way to verify certificate validity across multiple domains. It retrieves real certificate data and presents it clearly.

Monitoring infrastructure does not require complex systems. Sometimes a simple script provides the most effective solution.

Tags: AutomationCybersecurityLinuxPythonSSL
ShareTweetSharePinShareShareScan
ADVERTISEMENT
Jonathan Moore

Jonathan Moore

Senior Software Engineer and Cybersecurity Specialist with over 3 decades of experience in developing web, desktop, and server applications for Linux and Windows-based operating systems. Worked on numerous projects, including automation, artificial intelligence, data analysis, application programming interfaces, intrusion detection systems, streaming audio servers, WordPress plugins, and much more.

Related Articles

Monitoring Web Page Changes with Python

Monitoring Web Page Changes with Python

There are times when I need to know that a web page has changed without actively watching it. That might...

My SSH Setup: How I Manage Multiple Servers

My SSH Setup: How I Manage Multiple Servers

If you work with more than one server, the need to manage multiple servers with SSH becomes obvious pretty quickly....

Building a Network Tracker Auditor for Privacy with Python

Building a Network Tracker Auditor for Privacy with Python

In my last post, I dug into AdGuard, a robust ad blocker that tackles trackers and ads head-on. But how...

Recommended Services

Latest Articles

Monitor SSL Expiration with Python

Monitor SSL Expiration with Python

SSL certificates are one of those systems that work quietly in the background until something goes wrong. When a certificate...

Read moreDetails

Building a Simple WordPress Post List Tool with PHP

Building a Simple WordPress Post List Tool with PHP

I needed a quick way to view all my WordPress posts without logging into the admin dashboard. Sometimes you just...

Read moreDetails

Why Stable Websites Outperform Flashy Redesigns

Why Stable Websites Outperform Flashy Redesigns

Most websites do not fail in dramatic fashion. There is no explosion, no warning siren, no obvious moment where everything...

Read moreDetails

Monitoring Web Page Changes with Python

Monitoring Web Page Changes with Python

There are times when I need to know that a web page has changed without actively watching it. That might...

Read moreDetails
  • Privacy Policy
  • Terms of Service

© 2025 JMooreWV. All rights reserved.

No Result
View All Result
  • Home
  • Guides
    • Linux
    • Programming
      • JavaScript
      • PHP
      • Python
    • Tools
    • WordPress
  • Blog
    • Artificial Intelligence
    • Tutorials
    • Privacy
    • Security
  • Apps
    • Bible App
    • Bible Verse Screensaver
    • Blue AI Chatbot
    • Early Spring Predictor
    • FIGlet Generator
    • Password Generator
    • StegX
    • The Matrix
    • WeatherX
    • Website Risk Level Tool
  • About
    • About JMooreWV
    • Live Cyber Attacks
  • Contact
    • General Contact
    • Website Administration & Cybersecurity