Network and time
Check time, TLS, and certificates
Recognize how clock drift, certificate names, chains, validity, and renewal failures break otherwise healthy connections.
8 minute lesson
TLS validation depends on the requested name, a trusted chain, and current time. Break any of the three and connections fail with errors like certificate verify failed or certificate has expired — while ping, DNS, and TCP all look perfectly healthy. A working TCP handshake does not prove TLS or HTTP is correct.
Check the clock before the certificate
Certificate validity is checked against the local clock, so start with timedatectl:
timedatectl
# System clock synchronized: yes
# NTP service: active
If the clock is hours or days off, perfectly valid certificates look “expired” or “not yet valid” on this machine only. That’s the classic signature of clock drift: TLS fails here while every other client is fine. Fix time synchronization, not the certificate.
Inspect what the server presents
openssl s_client -connect app.example.com:443 -servername app.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -enddate -ext subjectAltName
subject=CN=app.example.com
notAfter=Oct 12 09:31:00 2026 GMT
X509v3 Subject Alternative Name:
DNS:app.example.com, DNS:www.example.com
Three things to verify in that output. The subjectAltName list must contain the exact name the client requested — clients match against SAN entries, not the CN. The notAfter date must be in the future. And the chain must verify: run the same command without the pipe and look for Verify return code: 0 (ok) near the end. Code 21 (unable to verify the first certificate) usually means the server sends its own certificate but not the intermediate — browsers may paper over it, strict clients won’t.
The -servername flag matters: it sets SNI, and a server hosting several sites returns different certificates for different names. Testing without it can show you a certificate no real client ever sees.
Separate the layers in one pass
curl -v https://app.example.com/ shows DNS resolution, TCP connect, the negotiated TLS version, certificate acceptance, and the HTTP status in order. Record DNS address, TCP success, negotiated TLS, certificate name, expiry, and HTTP status separately — knowing which line failed tells you which team or config file to open.
The trap: “fixing” a verification error with curl -k or by disabling verification in the app. That doesn’t fix anything, it removes the protection that was correctly telling you something is wrong, and it has a habit of getting committed. Diagnose with it if you must, never ship it. Renewal failures are the usual real cause — check the expiry date first, and check whether the renewal job (certbot timer or similar) has been failing quietly for weeks.
Lesson completed