Guide
Haproxy SSL certificate expired
Fix haproxy ssl certificate expired on frontends: bind crt paths, pem bundles, graceful reload, and openssl serial checks after ACME or commercial renew.
Updated May 30, 2026 802 words
haproxy ssl certificate expired on a bind line means the frontend still advertises a leaf past notAfter to clients—even when renewed PEM files sit in the same directory with a newer suffix nobody referenced in haproxy.cfg.
Load balancer teams use HAProxy for TCP and HTTP TLS termination at scale. Renewal automation often writes timestamped directories while the running process keeps crt paths aimed at the previous generation until someone edits config or symlink targets.
haproxy ssl certificate expired recovery is confirm live serial, update bind material or default cert directory, issue a graceful reload, and prove external TLS—not restart every backend application server behind the proxy.
Capacity events matter: autoscaling groups sometimes launch replacement HAProxy nodes from golden AMIs baked before the last renew. New members join the pool serving expired default crt directories while veterans look fine in spot checks. Include every anycast POP in post-renew verification, not only the node you SSH into.
Operations runbooks should state whether configuration management overwrites haproxy.cfg on reboot. Emergency PEM edits get wiped overnight when Puppet or Ansible restore old crt-list paths, resurrecting expired serials without human error the next morning.
What to check
Locate every bind directive using ssl and note whether HAProxy loads a single .pem bundle, a directory with multiple certs for SNI, or a crt-list file mapping hostnames to paths.
Compare on-disk notAfter from openssl against the certificate clients receive. HAProxy can mmap bundles at start; until reload, serial numbers stay on the old leaf.
Separate passthrough TCP mode from terminate SSL mode. Only terminate mode uses HAProxy’s certificate store; passthrough incidents belong on backend servers.
If multiple frontend sections share a default certificate directory, one expired symlink poisons every hostname without explicit per-name crt overrides.
HTTP strict transport security preload and mobile app pinning extend customer pain after renew fixes land in PEM files. Communicate proactive serial changes to mobile release managers when pins cover your HAProxy public keys indirectly through leaf certificates.
Step-by-step
Live TLS capture
HOST=www.example.com
echo | openssl s_client -connect "${HOST}:443" -servername "${HOST}" 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer -serial
Find bind and certificate directives
grep -E 'bind|crt |crt-list|ssl certificate' /etc/haproxy/haproxy.cfg
On some builds, included snippets live under conf.d—grep recursively.
Verify bundle on disk
openssl x509 -in /etc/haproxy/certs/example.pem -noout -dates -serial
Concatenated PEM bundles must place certificate first, then private key, then chain per HAProxy documentation.
Renew and install PEM
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
> /etc/haproxy/certs/example.pem
sudo chmod 640 /etc/haproxy/certs/example.pem
sudo chown root:haproxy /etc/haproxy/certs/example.pem
Update crt paths or crt-list entries when filenames change.
Validate configuration and reload
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
Use reload so existing sessions drain; avoid restart unless reload fails.
External verification
Re-run openssl; serial should differ from the incident opener when new material is live.
Zero-downtime reload on paired nodes
On active-active pairs, reload the standby peer first, fail traffic with health state toggles or BGP weight shifts, reload the former primary, then restore balance. Each step should include openssl through the VIP so you never assume reload success from local systemctl output alone.
How to read the output
haproxy -c configuration check errors must be zero before reload. Warnings about deprecated TLS keywords should not block renewal closes.
Expired notAfter on the first openssl trace is sufficient cause for the outage. After reload, matching subject and SAN with your crt-list host mapping proves SNI routing is correct.
When using a directory bind like bind *:443 ssl crt /etc/haproxy/certs/, HAProxy selects certificates by filename sort and SNI match—misnamed files land on the default cert clients see.
issuer changes flag CA migrations; update intermediate bundles inside the concat PEM to avoid chain errors after you fix expiry.
Common mistakes
Deploying renewed files without updating crt-list hostname-to-path mappings, leaving niche hostnames on expired dedicated files while the main site looks healthy.
Concatenating private key before certificate, causing haproxy -c to fail cryptically.
Reloading HAProxy before Certbot hooks finish writing keys with restrictive permissions HAProxy user cannot read post-reload.
Using bind ssl crt on the wrong frontend section when HTTP and HTTPS share a config template—edit the TLS frontend only.
Forgetting redundant nodes in an active-active pair where one peer never received the new PEM.
Ignoring reload haproxy after SSL renewal discipline when orchestration triggers reload on only one datacenter.
Stick tables and rate limits survive reload, but certificate mmap behavior varies by build—when in doubt, capture stats socket show ssl cert output before and after reload on production builds your vendor supports.
When to use automation
Pairs of HAProxy nodes and blue-green releases make disk checks unreliable. Only live TLS from each public VIP proves both peers serve renewed material.
ssldays queries live TLS for issuer, SANs, chain trust, and expiry on the hostname customers use—catching stale peers and stale load balancer attachments. RDAP adds domain registration context for ACME names. Alerts at 30, 14, 7, and 1 days align with short-lived Let’s Encrypt windows behind HAProxy. Email on all plans; Slack and Discord webhooks on Starter and Pro. Free: three domains, daily; Starter 25 hourly; Pro 100 hourly.
Validate each VIP on live check, then add hostnames at register. For path confusion after renew, read Where haproxy stores SSL certificates before the next rotation.
Frequently asked questions
Can one pem serve many domains?
Wildcard or multi-SAN certs can. Otherwise use crt-list for per-host PEM files.
What about HAProxy on Kubernetes?
Ingress controllers may generate config from Secrets—debug the running config map and pod reload, then external openssl.
Why do health checks pass?
TCP health checks without TLS do not observe certificate expiry. Add HTTPS monitors or external TLS probes.
Is terminate mode required for this guide?
Yes. TCP passthrough sends TLS to backends; fix certificates on those servers instead.
Monitor this hostname on a schedule
Live certificate checks and renewal reminders — not guesswork from PEM files.