- About
- Why tlsctl?
- Installation
- Quick start
- Basic usage
- Advanced usage
- Output formats
- Exit codes
- Status indicators
- Quiet mode
- Disabling color
- Configuration file
- Revocation checking
- Certificate fields
- Testing with badssl.com
- Shell completion
- License
tlsctl is a fast, zero-config command-line tool for inspecting TLS certificates from remote endpoints or local PEM files. Get instant visibility into certificate chains, expiry status, revocation state, and more — all from your terminal.
$ tlsctl client github.com
github.com (secure, expires in 52 days) ✓
Subject: CN=github.com
Issuer: CN=Sectigo Public Server Authentication CA DV E36,O=Sectigo Limited,C=GB
Validity: 2026-01-06 → 2026-04-05
SANs: github.com, www.github.com
Chain: github.com → Sectigo Public Server Authentication CA DV E36 → Sectigo Public Server Authentication Root E46 (3 certificates)
- Instant insights — One command shows certificate status, chain, SANs, and expiry at a glance
- Multiple output formats — Human-readable, JSON, YAML, verbose text, or raw PEM
- Revocation checking — Built-in CRL and OCSP support to detect revoked certificates
- PEM file parsing — Inspect local certificate files with the same rich output
- Custom CA support — Validate against private CAs with
--cacert - STARTTLS — Upgrade plaintext connections for SMTP, IMAP, POP3, and LDAP with
--starttls - SNI override — Set a custom server name with
--servernamefor IP-based targets with virtual hosts - Proxy aware — Connect through HTTP proxies with
--proxyor environment variables - Cross-platform — Pre-built binaries for Linux, macOS, and Windows (amd64 & arm64)
- Lightweight — Single static binary, no runtime dependencies
Download the latest release for your platform from the GitHub Releases page.
| Platform | Architecture | Archive |
|---|---|---|
| Linux | amd64 | tlsctl_1.0.0_linux_amd64.tar.gz |
| Linux | arm64 | tlsctl_1.0.0_linux_arm64.tar.gz |
| macOS | amd64 | tlsctl_1.0.0_darwin_amd64.tar.gz |
| macOS | arm64 | tlsctl_1.0.0_darwin_arm64.tar.gz |
| Windows | amd64 | tlsctl_1.0.0_windows_amd64.zip |
| Windows | arm64 | tlsctl_1.0.0_windows_arm64.zip |
# Example: install on Linux amd64
curl -sL https://github.com/catay/tlsctl/releases/latest/download/tlsctl_1.0.0_linux_amd64.tar.gz | tar xz
sudo mv tlsctl /usr/local/bin/Requires Go 1.25 or later.
git clone https://github.com/catay/tlsctl.git
cd tlsctl
make build # or: go build -o tlsctl .# Build the image
docker build -t tlsctl .
# Run
docker run --rm tlsctl client github.com
# Inspect a local PEM file (mount it into the container)
docker run --rm -v /path/to/cert.pem:/cert.pem:ro tlsctl pem /cert.pem# Inspect any TLS endpoint (port 443 is the default)
tlsctl client example.com
# Inspect multiple endpoints from a file (one per line)
tlsctl client --file hosts.txt
# Probe supported TLS versions and cipher suites
tlsctl client --tls-versions example.com
# Use a custom port
tlsctl client example.com:8443
# Inspect a local PEM file
tlsctl pem cert.pem$ tlsctl client badssl.com
*.badssl.com (secure, expires in 66 days) ✓
Subject: CN=*.badssl.com
Issuer: CN=R13,O=Let's Encrypt,C=US
Validity: 2026-01-20 → 2026-04-20
SANs: *.badssl.com, badssl.com
Chain: *.badssl.com → R13 (2 certificates)
$ tlsctl client expired.badssl.com
*.badssl.com (insecure, certificate expired, expires in -3958 days) ✗
Subject: CN=*.badssl.com,OU=Domain Control Validated+OU=PositiveSSL Wildcard
Issuer: CN=COMODO RSA Domain Validation Secure Server CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
Validity: 2015-04-09 → 2015-04-12
SANs: *.badssl.com, badssl.com
Chain: *.badssl.com → COMODO RSA Domain Validation Secure Server CA → COMODO RSA Certification Authority (3 certificates)
$ tlsctl client wrong.host.badssl.com
*.badssl.com (insecure, hostname mismatch, expires in 66 days) ✗
Subject: CN=*.badssl.com
Issuer: CN=R13,O=Let's Encrypt,C=US
Validity: 2026-01-20 → 2026-04-20
SANs: *.badssl.com, badssl.com
Chain: *.badssl.com → R13 (2 certificates)
$ tlsctl client self-signed.badssl.com
*.badssl.com (insecure, unknown authority, expires in 727 days) ✗
Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
Issuer: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
Validity: 2026-02-10 → 2028-02-10
SANs: *.badssl.com, badssl.com
$ tlsctl client untrusted-root.badssl.com
*.badssl.com (insecure, unknown authority, expires in 727 days) ✗
Subject: CN=*.badssl.com,O=BadSSL,L=San Francisco,ST=California,C=US
Issuer: CN=BadSSL Untrusted Root Certificate Authority,O=BadSSL,L=San Francisco,ST=California,C=US
Validity: 2026-02-10 → 2028-02-10
SANs: *.badssl.com, badssl.com
Chain: *.badssl.com → BadSSL Untrusted Root Certificate Authority (2 certificates)
$ tlsctl client incomplete-chain.badssl.com
*.badssl.com (insecure, unknown authority, expires in 66 days) ✗
Subject: CN=*.badssl.com
Issuer: CN=R13,O=Let's Encrypt,C=US
Validity: 2026-01-20 → 2026-04-20
SANs: *.badssl.com, badssl.com
$ tlsctl client --revocation crl revoked.badssl.com
revoked.badssl.com (secure, expires in 52 days) ✓
Subject: CN=revoked.badssl.com
Issuer: CN=E7,O=Let's Encrypt,C=US
Validity: 2026-01-06 → 2026-04-06
SANs: revoked.badssl.com
Revocation: REVOKED (CRL)
Chain: revoked.badssl.com → E7 (2 certificates)
$ tlsctl client --revocation ocsp google.com
*.google.com (secure, expires in 59 days) ✓
Subject: CN=*.google.com
Issuer: CN=WR2,O=Google Trust Services,C=US
Validity: 2026-01-19 → 2026-04-13
SANs: *.google.com, *.appengine.google.com, *.bdn.dev, *.origin-test.bdn.dev, *.cloud.google.com (+132 more)
Revocation: not revoked (OCSP)
Chain: *.google.com → WR2 → GTS Root R1 (3 certificates)
Use --tls-versions to probe supported TLS versions and enumerate server-side cipher suites in preferred order:
$ tlsctl client --tls-versions badssl.com
*.badssl.com (secure, expires in 66 days) ✓
Subject: CN=*.badssl.com
Issuer: CN=R13,O=Let's Encrypt,C=US
Validity: 2026-01-20 → 2026-04-20
SANs: *.badssl.com, badssl.com
TLS: TLS 1.2, TLS 1.3
Ciphers (TLS 1.2):
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
Ciphers (TLS 1.3):
TLS_AES_256_GCM_SHA384
Chain: *.badssl.com → R13 (2 certificates)
For TLS 1.0–1.2, cipher suites are enumerated by performing repeated handshakes to
determine the server's full preference order. For TLS 1.3, only the negotiated cipher
suite is reported because Go's crypto/tls does not allow configuring TLS 1.3 cipher
suites individually.
Use -o text for the full certificate details:
$ tlsctl client -o text badssl.com
[LEAF]
Version: 3
Serial Number: 06:17:1e:8f:ca:26:0a:2a:33:19:4f:1b:e5:a7:75:e1:c5:ed
Signature Algorithm: SHA256-RSA
Issuer: CN=R13,O=Let's Encrypt,C=US
Subject: CN=*.badssl.com
Not Before: 2026-01-20T20:02:51Z
Not After: 2026-04-20T20:02:50Z
Public Key Algorithm: RSA
Key Length: 2048 bits
Key Usage: Digital Signature, Key Encipherment
Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication
Basic Constraints: CA:FALSE
Subject Key ID: 2F:70:81:3B:C8:46:E1:26:35:CD:23:DB:C7:65:DA:30:CE:90:E7:44
Authority Key ID: E7:AB:9F:0F:2C:33:A0:53:D3:5E:4F:78:C8:B2:84:0E:3B:D6:92:33
Subject Alt Names: *.badssl.com, badssl.com
CA Issuers: http://r13.i.lencr.org/
CRL Distribution: http://r13.c.lencr.org/110.crl
[INTERMEDIATE]
Version: 3
Serial Number: 5a:00:f2:12:d8:d4:b4:80:f3:92:41:57:ea:29:83:05
Signature Algorithm: SHA256-RSA
Issuer: CN=ISRG Root X1,O=Internet Security Research Group,C=US
Subject: CN=R13,O=Let's Encrypt,C=US
Not Before: 2024-03-13T00:00:00Z
Not After: 2027-03-12T23:59:59Z
Public Key Algorithm: RSA
Key Length: 2048 bits
Key Usage: Digital Signature, Certificate Sign, CRL Sign
Extended Key Usage: TLS Web Client Authentication, TLS Web Server Authentication
Basic Constraints: CA:TRUE, pathlen:0
Subject Key ID: E7:AB:9F:0F:2C:33:A0:53:D3:5E:4F:78:C8:B2:84:0E:3B:D6:92:33
Authority Key ID: 79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E
CA Issuers: http://x1.i.lencr.org/
CRL Distribution: http://x1.c.lencr.org/
Use -o json for machine-readable structured output:
$ tlsctl client -o json badssl.com{
"certificates": [
{
"type": "leaf",
"version": 3,
"serial_number": "06:17:1e:8f:ca:26:0a:2a:33:19:4f:1b:e5:a7:75:e1:c5:ed",
"signature_algorithm": "SHA256-RSA",
"issuer": "CN=R13,O=Let's Encrypt,C=US",
"subject": "CN=*.badssl.com",
"common_name": "*.badssl.com",
"not_before": "2026-01-20T20:02:51Z",
"not_after": "2026-04-20T20:02:50Z",
"public_key_algorithm": "RSA",
"key_length": 2048,
"key_usage": ["Digital Signature", "Key Encipherment"],
"extended_key_usage": ["TLS Web Server Authentication", "TLS Web Client Authentication"],
"subject_alternative_names": ["*.badssl.com", "badssl.com"],
"fingerprint": {
"sha1": "71:03:e9:e6:7c:f1:0e:e0:a7:29:28:fe:85:49:a6:4f:e5:66:e0:48",
"sha256": "b4:5a:53:24:32:d9:8f:62:b6:ea:f1:47:32:06:10:f1:..."
}
}
],
"verified": true
}Use -o yaml for YAML-formatted output:
$ tlsctl client -o yaml badssl.comcertificates:
- type: leaf
version: 3
serial_number: 06:17:1e:8f:ca:26:0a:2a:33:19:4f:1b:e5:a7:75:e1:c5:ed
signature_algorithm: SHA256-RSA
issuer: CN=R13,O=Let's Encrypt,C=US
subject: CN=*.badssl.com
common_name: '*.badssl.com'
not_before: "2026-01-20T20:02:51Z"
not_after: "2026-04-20T20:02:50Z"
public_key_algorithm: RSA
key_length: 2048
key_usage:
- Digital Signature
- Key Encipherment
subject_alternative_names:
- '*.badssl.com'
- badssl.com
verified: trueUse -o raw to extract the PEM-encoded certificates:
# Save the certificate chain to a file
tlsctl client -o raw badssl.com > chain.pem
# Pipe to openssl for further inspection
tlsctl client -o raw badssl.com | openssl x509 -noout -textUse -k or --insecure to skip TLS certificate verification entirely:
tlsctl client -k self-signed.example.com
tlsctl client --insecure internal.example.comUse --cacert to validate certificates against a private CA:
tlsctl client --cacert /path/to/internal-ca.pem internal.example.com
tlsctl pem --cacert /path/to/ca.pem server-cert.pemConnect through an HTTP proxy with --proxy (or -x):
tlsctl client --proxy http://proxy.corp.example.com:8080 example.com
tlsctl client -x http://proxy:3128 example.comThe --proxy flag falls back to HTTPS_PROXY / HTTP_PROXY environment variables if not set.
Use --servername to override the TLS Server Name Indication (SNI) value. This is useful when connecting to an IP address that hosts multiple virtual hosts:
tlsctl client --servername example.com 93.184.216.34:443Use --starttls to negotiate a plaintext-to-TLS upgrade before inspecting the certificate. Supported protocols: smtp, imap, pop3, ldap.
When --starttls is used without an explicit port, the default port for the protocol is used automatically:
| Protocol | Default port |
|---|---|
smtp |
587 |
imap |
143 |
pop3 |
110 |
ldap |
389 |
# SMTP STARTTLS (connects to port 587 by default)
tlsctl client --starttls smtp mail.example.com
# SMTP on a custom port
tlsctl client --starttls smtp mail.example.com:25
# IMAP STARTTLS
tlsctl client --starttls imap mail.example.com
# POP3 STARTTLS
tlsctl client --starttls pop3 mail.example.com
# LDAP STARTTLS
tlsctl client --starttls ldap ldap.example.com# Parse a single certificate
tlsctl pem cert.pem
# Parse a certificate chain (multiple certs in one file)
tlsctl pem chain.pem
# Read PEM data from stdin (use '-' or omit the file argument)
cat cert.pem | tlsctl pem -
cat chain.pem | tlsctl pem
# Fetch a certificate via openssl and inspect it
echo | openssl s_client -connect github.com:443 2>/dev/null | openssl x509 | tlsctl pem -
# Inspect a certificate fetched via curl
curl -s https://example.com/ca.pem | tlsctl pem -
# Verbose output
tlsctl pem -o text cert.pem
# JSON output
tlsctl pem -o json cert.pem
# YAML output
tlsctl pem -o yaml cert.pem
# CRL revocation check on a PEM file
tlsctl pem --revocation crl cert.pemThe JSON output (-o json) pairs well with jq for extracting specific fields:
# Get the SHA-256 fingerprint of the leaf certificate
tlsctl client -o json example.com | jq -r '.certificates[] | select(.type == "leaf") | .fingerprint.sha256'
# List all Subject Alternative Names (SANs)
tlsctl client -o json example.com | jq -r '.certificates[] | select(.type == "leaf") | .subject_alternative_names[]'
# Show expiry date for each certificate in the chain
tlsctl client -o json example.com | jq -r '.certificates[] | "\(.type): \(.common_name) expires \(.not_after)"'
# Check if the certificate is verified
tlsctl client -o json example.com | jq '.verified'
# Extract the issuer and subject of the leaf certificate
tlsctl client -o json example.com | jq '.certificates[] | select(.type == "leaf") | {subject, issuer}'
# Get serial numbers of all certificates in the chain
tlsctl client -o json example.com | jq -r '.certificates[] | "\(.type): \(.serial_number)"'
# Count the number of SANs on the leaf certificate
tlsctl client -o json example.com | jq '.certificates[] | select(.type == "leaf") | .subject_alternative_names | length'
# Check multiple hosts and report their expiry dates
tlsctl client -o json google.com github.com | jq -r '.[] | .certificates[] | select(.type == "leaf") | "\(.common_name) expires \(.not_after)"'| Format | Flag | Description |
|---|---|---|
| Human | (default) or -o human |
Brief human-readable summary with color-coded status |
| Text | -o text |
Verbose output with all certificate fields |
| JSON | -o json |
Full structured JSON, ideal for scripting and automation |
| YAML | -o yaml |
Full structured YAML |
| Raw | -o raw |
PEM-encoded certificates |
0ok1runtime error (e.g., connection or parsing failure)2insecure or invalid (unverified, expired, or revoked)3revocation error (revocation check failed)4expiring soon (certificate expires within 30 days, configurable via--expiry-warning)
The default output uses color-coded status indicators:
| Indicator | Color | Meaning |
|---|---|---|
✓ secure |
Green | Certificate is valid and verified |
⚠ secure |
Yellow | Certificate is verified but expires within the warning threshold (default: 30 days, configurable via --expiry-warning) |
✗ insecure |
Red | Certificate verification failed (with reason) |
Use -q or --quiet to suppress all informational and warning output. Only error messages are displayed. Exit codes are preserved, making this ideal for scripting and monitoring:
tlsctl client -q example.com
echo $? # 0 = ok, 2 = insecure, 4 = expiring soon, etc.Use --no-color to strip ANSI color codes from output, useful for piping to other tools or log ingestion:
tlsctl client --no-color example.com
tlsctl client --no-color example.com | tee cert.logtlsctl supports a settings.json configuration file for defining default values per subcommand. Configuration values are applied in the following order of precedence:
- CLI arguments (highest priority)
- Values from
settings.json - Built-in defaults (lowest priority)
The configuration file is stored in the OS-specific configuration directory:
| Platform | Path |
|---|---|
| Linux | $XDG_CONFIG_HOME/tlsctl/settings.json or ~/.config/tlsctl/settings.json |
| macOS | ~/Library/Application Support/tlsctl/settings.json |
| Windows | %AppData%\tlsctl\settings.json |
Use --config <path> to override the default location:
tlsctl --config /path/to/settings.json client example.comIf the default configuration file is missing, tlsctl runs with built-in defaults. If --config points to a missing file, an error is reported.
{
"global": {
"no-color": false,
"quiet": false
},
"client": {
"expiry-warning": 21,
"output": "json",
"proxy": "http://proxy:8080",
"tls-versions": true,
"revocation": "ocsp",
"revocation-timeout": "10s",
"revocation-soft-fail": false
},
"pem": {
"expiry-warning": 7,
"output": "yaml",
"cacert": "/etc/ssl/certs/ca.pem"
}
}The global section applies to all subcommands. Each subcommand section (client, pem) supports the same keys as the corresponding CLI flags. Only set the values you want to override — omitted keys use built-in defaults.
Invalid JSON, unknown keys, and invalid values (e.g., out-of-range expiry-warning) produce clear error messages.
Both client and pem subcommands support certificate revocation checking via the --revocation flag.
| Flag | Default | Description |
|---|---|---|
--revocation |
Revocation check mode: crl or ocsp (disabled if not set) |
|
--revocation-timeout |
5s |
Timeout for revocation requests |
--revocation-soft-fail |
true |
Treat unreachable revocation endpoints as non-fatal |
# CRL-based revocation check
tlsctl client --revocation crl example.com
# OCSP-based revocation check
tlsctl client --revocation ocsp example.com
# Custom timeout for slow networks
tlsctl client --revocation ocsp --revocation-timeout 10s example.comThe tool extracts and displays the following X.509 certificate fields:
| Field | Description |
|---|---|
| Type | Leaf, intermediate, or root |
| Version | X.509 certificate version |
| Serial Number | Hex-formatted serial number |
| Signature Algorithm | e.g., SHA256-RSA, ECDSA-SHA256 |
| Issuer / Subject | Distinguished name (DN) |
| Validity | Not Before / Not After in RFC 3339 format |
| Public Key Algorithm | e.g., RSA, ECDSA |
| Key Length | Public key size in bits (e.g., 2048, 4096, 256) |
| Key Usage | Digital Signature, Key Encipherment, Certificate Sign, etc. |
| Extended Key Usage | TLS Web Server Authentication, Client Authentication, etc. |
| Basic Constraints | CA flag and path length |
| Subject / Authority Key ID | Hex-formatted key identifiers |
| Subject Alt Names | DNS names |
| Email / IP Addresses | Additional identifiers |
| OCSP / CA Issuers / CRL | Revocation and issuer endpoints |
| Revocation Status | CRL or OCSP result (when --revocation is enabled) |
| Fingerprint | SHA-1 and SHA-256 fingerprints |
badssl.com provides various endpoints for testing TLS certificate scenarios. Here are some useful ones to try with tlsctl:
# Valid certificate
tlsctl client badssl.com
# Expired certificate
tlsctl client expired.badssl.com
# Wrong hostname
tlsctl client wrong.host.badssl.com
# Self-signed certificate
tlsctl client self-signed.badssl.com
# Untrusted root CA
tlsctl client untrusted-root.badssl.com
# Incomplete certificate chain
tlsctl client incomplete-chain.badssl.com
# Revoked certificate (check via CRL)
tlsctl client --revocation crl revoked.badssl.com
# ECC certificate
tlsctl client ecc256.badssl.comGenerate autocompletion scripts for your shell:
# Bash
tlsctl completion bash > /etc/bash_completion.d/tlsctl
# Zsh
tlsctl completion zsh > "${fpath[1]}/_tlsctl"
# Fish
tlsctl completion fish > ~/.config/fish/completions/tlsctl.fish