haproxy backend health check 失败但 curl 直连正常的协议/路径坑

22次阅读

HAProxy 健康检查失败而 curl 正常,主因是协议 / 路径不一致:HTTP/1.0 无 Host 头被后端拒收、HTTPS 后端误用 HTTP 检查、重定向未被处理、或源 IP 被 ACL 拦截,需针对性配置 httpchk、ssl-hello-chk、状态码容错及网络策略。

haproxy backend health check 失败但 curl 直连正常的协议 / 路径坑

HAProxy 后端 健康检查失败,但用 curl 直连服务却完全正常——这是很典型的“协议 / 路径不一致”导致的误判,不是服务真挂了,而是 HAProxy 的健康检查请求没被后端正确识别或响应。

HTTP 健康检查默认发的是 HTTP/1.0,且 Host 头可能为空

HAProxy 默认的 option httpchk 会发送一个简化的 HTTP/1.0 请求(无 Host 头、无 User-Agent),例如:

GET /health HTTP/1.0rnrn

而很多现代 Web 服务(如 Nginx、Spring Boot、某些 API 网关)在收到无 Host 头或 HTTP/1.0 请求时,会直接返回 400、421 或直接关闭连接,导致 HAProxy 判定为失败。

解决方法

  • 显式指定 HTTP/1.1 + Host 头:
    option httpchk GET /health HTTP/1.1rnHost: example.com
  • 在 backend 中配置:
    http-check send hdr Host example.com
    http-check expect status 200

后端要求 TLS,但 health check 走的是明文 HTTP

如果后端只监听 HTTPS(如 443 端口),而 HAProxy 的 health check 配置仍用 httpchk 发送 HTTP 请求到 443,TCP 层能通,但 TLS 握手前就发了 HTTP 请求,服务端通常直接 reset 连接,HAProxy 收不到任何 HTTP 响应,判定为 down。

解决方法:

  • 改用 option ssl-hello-chk(仅检测 TLS 握手是否成功,不校验内容)
  • 或启用 HTTPS 健康检查(需 HAProxy ≥ 2.2):
    option httpchk GET /health
    http-check send hdr Host example.com
    http-check expect status 200
    http-check expect ! rstring ^HTTP/1.[01] [45]

    并确保 backend 使用 ssl 关键字和对应证书验证配置

健康检查路径返回 301/302 重定向,但 HAProxy 不跟随

比如后端把 /health 重定向到 /api/v1/health,而 HAProxy 默认不处理重定向(http-check 只看第一个响应的 状态码),收到 301 就判定失败。

解决方法:

  • 避免在健康检查路径上做重定向,直接返回 200
  • 或显式允许特定跳转码:
    http-check expect status 200 301 302

后端服务监听 localhost 或特定 IP,但 health check 源地址被 防火墙/ACL 拦截

HAProxy 发起 health check 时使用的是本机任意可用源 IP(非 127.0.0.1),若后端有 iptables、cloud security group 或应用层白名单(如 Spring Security 的 allowed-origins),可能拒绝非预期来源的请求,而 curl localhost 因走 loopback 成功,造成错觉。

解决方法:

  • 检查后端访问日志,确认 health check 请求是否到达;对比源 IP
  • 临时放宽 ACL 或将 HAProxy 所在主机 IP 加入白名单
  • tcpdump 抓包验证:在后端机器上执行 tcpdump -i any port ,再观察 HAProxy 日志触发 check 时是否有 SYN 包进来

text=ZqhQzanResources