SSL handshake failure after idle with ca-file + verify optional — browser sends FIN after CertificateRequest

Hi, I need help with reconnection handshake errors.

After an idle period (5–10 minutes), browsers consistently fail the first TLS handshake to an HAProxy frontend configured with ca-file and verify optional. The retry always succeeds immediately.

Environment: HAProxy 2.9.14, OpenSSL 3.0.16, FreeBSD 15.0-CURRENT (pfSense)

Bind line:

bind :443 ssl crt /path/to/cert.pem ca-file /path/to/ca.pem verify optional crt-ignore-err all

tcpdump capture shows:

  1. Browser opens new TCP connection after idle (new source port)

  2. Sends small ClientHello (267 bytes — PSK resumption attempt)

  3. Server responds with ServerHello + CertificateRequest (3264 bytes)

  4. Browser immediately sends FIN — closes connection

  5. Browser retries with full ClientHello (1828 bytes, includes client certificate)

  6. Handshake succeeds

What I tested (all still fail):

  • verify optional vs verify required — same behavior

  • Removing no-tls-tickets from bind and globally — same behavior

  • tune.ssl.cachesize 0 + tune.ssl.lifetime 0 — same behavior

  • TLS 1.2 only (ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.2) — same behavior

  • Direct bind on WAN IP (no TCP proxy/PROXY protocol) — same behavior

What fixes it:

  • Removing ca-file from bind completely eliminates the issue

Conclusion: The mere presence of ca-file causes HAProxy/OpenSSL to send CertificateRequest during the handshake. After idle, the browser attempts PSK resumption without a client certificate ready, receives CertificateRequest, and aborts. This appears to be browser behavior (tested with Chrome/Edge), but I’m wondering:

  1. Is there a way to suppress CertificateRequest during session resumption while keeping ca-file for new connections?

  2. Should verify optional prevent sending CertificateRequest when the client hasn’t offered a certificate in ClientHello?

  3. Is this a known interaction between OpenSSL 3.0 session resumption and post-handshake authentication?

Workaround: I’m using a TCP frontend that routes whitelisted IPs to a backend without ca-file, and other IPs to the mTLS backend. This works but is complex.

Session resumption with mutual TLS is very difficult to get right.

I don’t know enough about it, but I suggest filing a github issue about this: