SSL Expiry
Check TLS certificate expiration dates on port 443.
lupaxa-ssl-expiry connects to each host, reads the leaf certificate the
server presents, and reports the issuer, the expiration date, and how many
days remain. The handshake requires TLS 1.2 or newer. Hostname matching and
certificate trust are not checked, so an expired, self-signed, or untrusted
leaf is still reported. The TCP port is always 443.
Requirements
- Python 3.13 or newer
- Outbound TCP access to port 443
cryptography and prettytable install with the package.
Install
pip install lupaxa-ssl-expiry
ssl-expiry --help
CLI
ssl-expiry example.com example.org
ssl-expiry example.com,example.org
ssl-expiry --file hosts.txt --warn-days 14
printf '%s\n' example.com example.org | ssl-expiry --file -
ssl-expiry --sort days --order descending example.com example.org
python -m lupaxa.ssl_expiry --version
Pass hosts as separate arguments, or separate them with commas. International names are converted to ASCII before the connection, and that form is what the host column prints. IPv4 and IPv6 addresses are accepted. A URL, a port suffix, or any other non-host is invalid and is not connected.
--file reads one hostname per line. Blank lines and # comments are
ignored. - reads stdin. Repeat --file to combine lists. Hosts on the
command line are checked first, then hosts from each file.
--timeout is the TCP connect and TLS handshake timeout in seconds
(default 10). It must be greater than 0.
--warn-days is how close a date can be before the command exits 1
(default 30). The comparison is inclusive: a certificate with 30 days
left exits 1 when --warn-days is 30. 0 means only today or earlier.
--sort orders the table by host, issuer, date, or days.
--order is ascending (the default) or descending. asc and desc
are accepted. Host and issuer order ignore case. Date order uses the
expiration date, including hosts that show Expired. Day order uses the
day count. Rows with no issuer, date, or day count stay at the end.
Without --sort, rows stay in the order the hosts were given. --order
without --sort exits 2.
With no hosts, the command prints help and exits 2.
Output
Results print as one table. Every host is a row, including invalid hosts and connection failures.
+--------------+---------------+---------------------+------+
| Host | Issuer | Date | Days |
+--------------+---------------+---------------------+------+
| example.com | Let's Encrypt | 15th March 2027 | 173 |
| example.org | DigiCert Inc | 22nd September 2026 | 0 |
| old.example | Let's Encrypt | Expired | -12 |
| slow.test | — | timed out | — |
| nope.invalid | — | Invalid Host | — |
+--------------+---------------+---------------------+------+
The date is the day, with st, nd, rd, or th, then the month name
and the year, as in 11th November 2026. Expired means that date is
already past. Days is the number of UTC calendar days remaining. 0
means the certificate expires today. A negative number is how many days
ago it expired.
The issuer is the certificate organization name, or its common name when
the organization is absent. When neither is present, the issuer column
shows —.
Invalid Host means the value was not connected. A connection failure,
including a timeout or a handshake that yields no certificate, puts the
cleaned error in the date column. Examples include timed out and
no peer certificate.
Exit Codes
| Code | When |
|---|---|
0 |
Help, version, or every certificate is further out than --warn-days |
1 |
A certificate is due, expired, or the host is invalid |
2 |
Usage failed, a file could not be read, no hosts were given, or a connection failed |
When one host needs attention and another connection fails, the command
exits 1.
Library
from lupaxa.ssl_expiry import lookup
result = lookup("example.com", timeout=10.0)
if result.status == "expires":
print(result.issuer, result.expiration, result.days_remaining)
else:
print(result.status, result.error)
lookup returns a result for invalid hosts and connection failures.
status is expires, invalid, or error. days_remaining is set only
for expires. error is set only for error, and is a single line. An
invalid host is not connected. A timeout that is not greater than 0
raises ValueError.
Pass now to fix the clock used for days_remaining. Pass probe to
replace the network call in tests. The callable receives the normalized
host and the timeout in seconds, and returns a LeafCert.
Development
make init
make python-install-dev
make python-check
Metadata
Release files for lupaxa-ssl-expiry 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lupaxa_ssl_expiry-0.1.4.tar.gz | 10.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lupaxa_ssl_expiry-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.9 kB
Release files / lupaxa_ssl_expiry-0.1.4.tar.gz
| Download URL | lupaxa_ssl_expiry-0.1.4.tar.gz |
|---|---|
| Size | 10.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
39cc961b2714a2b2a639317afb072e3c151f5b07b4baf17b1618e96b06e61d34
|
|
BLAKE2b-256 checksum How to use checksums |
fcc786428a0bb84f120db7c87edc8a8546eeefb9d9e54f05970256ae2df81ee2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / lupaxa_ssl_expiry-0.1.4-py3-none-any.whl
| Download URL | lupaxa_ssl_expiry-0.1.4-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
27b837c8251052de66911e9bb270374dd512d6cbc2166d116bc7bd9159f8073c
|
|
BLAKE2b-256 checksum How to use checksums |
cf0282a8179c9b1f3a230ad28baf0e08991adc41a10f8dd0d795c99360f2a96e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log