Leaf Certificate Valid, Chain Expired: Read the Depth
An expiry error can belong to an issuer, not the leaf. Read the verification depth and trust inputs before renewing a certificate that still looks current.
You check the server certificate, see months left on the clock, and the client still refuses the connection with an expiry message. The date you read belongs to the leaf, and the complaint may be about something deeper in the path. Verification walks a chain, and any certificate that walk selects can fail its own validity window. Read which one failed before you renew anything.
Depth names the certificate that failed
In OpenSSL diagnostics, depth 0 is the target certificate and depth 1 is the issuer that signed it, with higher numbers continuing toward the trust anchor. An expiry reported at depth 1 is not contradicted by a current notAfter on the leaf, so checking the visible certificate again will never explain the error.
Record the failing certificate's fingerprint along with its subject, because different certificates can carry the same CA name. Cross-signing produces exactly that situation: the same authority appears more than once, with different validity dates, and the verifier selected the one you did not expect. A familiar label is not an identifier, and a fingerprint is what distinguishes two objects that share a name.
Watch for products with more than one certificate role as well. On OpenVPN Access Server, the certificates protecting the web interfaces are separate from those used for VPN connections, so a browser that opens the admin page happily proves nothing about the tunnel's path.
Trust inputs are not the same as chain candidates
Give the verifier the two inputs separately and explicitly. The trust file holds the anchors you accept; the untrusted bundle holds intermediate certificates offered for building the path. Mixing them is how people accidentally promote an intermediate into a root and then wonder why the same error follows them between machines.
$ openssl verify -verbose -show_chain -purpose sslserver \
-CAfile trusted-roots.pem -no-CApath -no-CAstore \
-untrusted intermediates.pem server.pem
Disabling the default path and store with -no-CApath -no-CAstore keeps the test honest: the result then depends on the files you named rather than on whatever the workstation happens to trust. The OpenSSL verification reference documents these options and the diagnostics they produce.
-show_chain prints the built chain when verification succeeds, which is useful for confirming that the path is the one you intended. When it fails, keep the reported depth and subject; those two fields tell the next reader which certificate to inspect.
What this does not do is reproduce every client's policy. A pin, a purpose restriction or a client-specific identity check lives in the client, not in this command. Those are the settings to inspect when verification passes on the server and the connection still fails. Keep the two diagnoses separate, or you will renew a healthy certificate chasing a pinning error.
What the check proves and what it leaves out
This is a path-and-purpose check against a trust file you supplied. For an HTTPS name check, add the expected hostname through the appropriate option; for a VPN connection, keep the client's configured identity checks and use the trust material that client actually reads.
What a server sends is a set of candidates, not the path your client will end up building. Local trust and the library's construction rules both shape the result, which is why the same response can verify on one machine and fail on another. Compare the failing client's maintained trust store and TLS implementation with a working client's before assuming identical evaluation. A difference there explains many "only this laptop fails" reports.
To see each certificate's window rather than one at a time, print them as a set:
$ openssl x509 -in server.pem -noout -subject -dates
$ openssl crl2pkcs7 -nocrl -certfile intermediates.pem \
| openssl pkcs7 -print_certs -noout
That second command lists subjects and issuers without decoding each file by hand, which is how you spot an intermediate whose date has passed while the leaf still looks fine.
Repair the path without weakening verification
When an expired intermediate or an obsolete cross-signed certificate is being supplied, obtain the issuer's currently supported chain and deploy it through the service's documented certificate workflow. When the client lacks the intended maintained anchor, update its managed trust configuration or the client software. Resist the shortcut of marking every certificate in the bundle as a root just to silence the message.
Root handling deserves a note, because summaries often overstate it. RFC 5280 treats the anchor as an input that sits outside the prospective path, while OpenSSL documents validity checks that include its root CA certificate. Both statements are true of their own systems; generalising one library's root-expiry behaviour to every client produces confident wrong answers.
After the change, force a fresh authentication on the affected client and confirm that the specific expiry message is gone. Renew the leaf only if the leaf itself is unsuitable. Disabling time checks, or restoring a historical cross-certificate, does not produce a valid current path; it just delays the next failure.