Skip to content
ssldays

Guide

TLS version vs certificate expiry (different checks)

TLS version support and certificate notAfter are separate failure modes. Learn how to test protocol minimums without confusing them with expiry dates.

Updated Apr 10, 2026 1,146 words

tls version vs certificate expiry trips up triage: clients can negotiate TLS 1.3 with a perfectly modern cipher suite and still fail when notAfter is yesterday.

Platform engineers harden listeners by disabling TLS 1.0 while certificate owners renew annually. Support tickets blend “cannot connect” with “certificate expired” because both surface as TLS handshake failures in crude logs.

The mistake is fixing protocol settings when the leaf date is past, or renewing the cert while ancient clients still cannot negotiate any enabled version. Another is trusting SSL Labs protocol badges while ignoring an imminent notAfter.

You will run separate probes: one that reads certificate dates on the live chain, and one that tests which protocol versions the server accepts—without conflating the two outcomes.

Incident bridges benefit from a verbal script: ask whether the browser names a date or a protocol. If the message mentions expiration, run the date probe first; if it mentions protocol version, validate client capabilities second.

What to check

Certificate expiry is encoded in the X.509 leaf (and each intermediate) as notBefore and notAfter. After expiry, verification fails before your application layer runs, regardless of TLS 1.3 support.

TLS version policy is server configuration: which protocol versions and cipher suites the listener offers during ClientHello negotiation. A server can require TLS 1.2+ while presenting a cert valid for ninety more days—or allow TLS 1.0 with an already expired cert.

Browsers and tools report different errors: date invalid / certificate has expired for lifetime problems versus protocol version or handshake failure when versions do not overlap. Capture the exact error string.

Also check intermediate expiry: an expired intermediate breaks trust even when the leaf date looks fine. Read the full chain with -showcerts, not only the first certificate.

HSTS, pinning, and corporate proxies can mask or amplify version issues separately from expiry. Treat them as follow-up work after you prove dates on the wire.

Regulatory frameworks sometimes mandate TLS 1.2 minimum while auditors also ask for “valid certificate through fiscal year.” Schedule separate calendar reminders so version projects do not distract from ACME timer failures.

Java 8 clients and legacy embedded devices often force TLS version conversations during the same week a cert expires—tag tickets with version vs date in the subject line so parallel workstreams do not overwrite each other’s fixes.

Step-by-step

1. Read certificate dates (expiry check)

HOST=example.com
echo | openssl s_client -connect "${HOST}:443" -servername "${HOST}" -showcerts 2>/dev/null \
  | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/'   | openssl x509 -noout -dates -subject -issuer 2>/dev/null

SNI must match the hostname you type in the browser.

Repeat parsing for each PEM block if multiple certificates appear, or save the chain to a file and inspect each cert’s notAfter.

For a focused leaf-only read:

echo | openssl s_client -connect "${HOST}:443" -servername "${HOST}" 2>/dev/null \
  | openssl x509 -noout -dates -serial

SNI must match the hostname you type in the browser.

2. Probe supported TLS versions (configuration check)

for v in tls1 tls1_1 tls1_2 tls1_3; do
  printf "%s: " "$v"
  echo | openssl s_client -connect "${HOST}:443" -servername "${HOST}" "-${v}" 2>&1 \
    | grep -E 'Protocol|alert handshake failure|SSL handshake has read' | head -n 2
done

SNI must match the hostname you type in the browser.

On OpenSSL builds without certain versions, skip unsupported flags and note tool limitations.

3. Correlate with HTTP client

curl -vI --tlsv1.2 --connect-timeout 15 "https://${HOST}/" 2>&1 | grep -Ei 'expire|SSL|TLS'

SNI must match the hostname you type in the browser.

curl -vI --tls-max 1.3 "https://${HOST}/" 2>&1 | grep -Ei 'expire|SSL|TLS'

SNI must match the hostname you type in the browser.

If curl reports expiry, stop chasing TLS version until renewal completes. If curl succeeds on 1.2 but legacy clients need 1.0, that is a policy project—not an ACME renewal.

PowerShell note: use curl.exe on Windows; TLS flags map to the libcurl/OpenSSL build bundled with your install.

Compare protocol findings with SSL Labs test and certificate expiry when you need a readable matrix for management.

nginx and Apache logs may log SSL_do_handshake failures without distinguishing alert types. Correlate log timestamps with simultaneous openssl runs instead of guessing from sparse error strings.

How to read the output

s_client prints depth and verify return codes. Expiry problems often appear as certificate has expired in verify messages even before you parse dates manually.

notAfter= on the leaf is the customer-facing renewal deadline. Intermediates expiring sooner can break Android or older trust stores while desktop Chrome still works—do not ignore middle certificates.

Protocol probes return Protocol : TLSv1.3 (or similar) when negotiation succeeds. handshake failure on TLS 1.0 while TLS 1.2 works means the server correctly refuses old versions—not an expiry issue.

curl distinguishes verification errors (dates, trust) from connection errors (version mismatch, cipher). Read the first failure line in verbose output; do not assume all TLS errors mean expired certs.

Document which tests you ran in the ticket so responders do not repeat version tuning when the leaf is simply past notAfter.

0-RTT early data settings and TLS version caps interact in HTTP/3 stacks. Rarely confuse QUIC handshake errors with cert expiry, but capture both when supporting bleeding-edge ingress controllers.

Windows Schannel clients surface distinct HRESULTs for expired certs versus disabled protocols. Note client OS in tickets when reproducing from mixed environments.

Common mistakes

Disabling TLS 1.0 to fix an expiry incident wastes time. Renew and reload the chain first; schedule version deprecation after clients are inventoried.

Renewing a certificate but leaving an expired intermediate in the bundle produces “works in OpenSSL with partial chain” fantasies while browsers fail—update the fullchain file.

Assuming TLS 1.3-only load balancers automatically renew at the edge: managed platforms still have notAfter on presented certs. Check the edge name your users resolve.

Using outdated OpenSSL on a jump host that cannot speak TLS 1.3, then blaming the server for handshake failure when only the client is ancient.

Turning on TLS inspection appliances without re-trusting corporate roots makes every site look broken—including your own renewal dashboards. Fix trust stores before interpreting server-side expiry.

When to use automation

Monitor notAfter via live handshakes on every production hostname—that is independent of TLS version policy. Track version support in quarterly config reviews or SSL Labs snapshots, not as midnight pages.

Alert on verification failures separately from synthetic “TLS 1.0 connect” tests if you still support legacy clients. ssldays-style monitoring emphasizes expiry and external presentation; configuration baselines belong in IaC tests or SSL Labs exports.

After renewals, rerun date checks before closing incidents referenced in curl SSL certificate expired playbooks. After hardening, rerun version probes so marketing’s old Android webviews are not surprised.

Infrastructure-as-code modules should expose two test hooks: assert_cert_not_expiring on live URLs and assert_min_tls_version in staging—never one boolean pretending to cover both.

When customers demand PCI language, cite certificate validity and disallowed protocols in separate PDF sections to match assessor checklists.

Penetration testers often file TLS version findings alongside expired staging certs. Triage production customer paths first; staging outliers should not delay public leaf renewal.

Cloud load balancers expose security policy objects separate from listener certificates. IAM changes to policy can disable TLS 1.0 without touching ACM renewal timers—document which team owns each knob.

Mobile apps bundling old BoringSSL builds may not negotiate TLS 1.3 even when the server allows it; that is unrelated to server cert expiry but shows up in the same support queue after marketing posts hardening blog posts.

Use testssl.sh (if permitted) only when you need cipher-level matrices; for expiry incidents its output is heavier than necessary compared with focused date commands.

Database protocols (Postgres, Redis TLS) reuse X.509 expiry rules but different ports—do not assume your HTTPS openssl loop covers data stores listening on 5432 or 6380 with distinct cert mounts.

Observability agents that scrape HTTPS metrics endpoints inherit TLS verification settings from the agent config; expired certs break metrics before they break the storefront, hiding outages until SLO dashboards go blank.

Frequently asked questions

My site enforces TLS 1.2+; do I still need expiry monitoring?

Yes. Version policy does not extend certificate lifetime. Short-lived ACME certs demand automated renewal regardless of TLS minimums.

SSL Labs shows TLS 1.0 disabled but browsers say expired?

Trust the expiry finding first. Grades and protocol tables do not contradict an expired leaf—they coexist until you renew.

Does disabling TLS 1.0 affect certbot renewals?

HTTP-01 validation may use the validator’s TLS stack, not your disabled protocols on customer traffic. Renewal failures are usually challenge-related, not version-related.

How do I test both quickly?

Run the s_client dates command, then one curl -vI without insecure flags. Only if both pass do you spend time on version matrices or cipher audits.

See what the server presents right now

Free live certificate check — no signup.