// Engineering Log

Proxy servers: Part 3 — HAProxy

Published on 2026-09-21

HAProxy (High Availability Proxy) — a load balancer and reverse proxy for TCP and HTTP with open source. Unlike nginx, HAProxy does not serve files or run application code: its task is to accept connections, distribute them between servers, monitor server health, and apply rules to traffic. In addition to the free version, HAProxy Technologies provides a commercial HAProxy Enterprise.

Branches with even numbers have long-term support (LTS) for five years. As of September 2026, the relevant LTS branches are 3.2 (released May 2025) and 3.4 (June 2026). For production servers you should use the LTS version from your distribution repository or the official packages for it.

Key configuration concepts

Configuration is stored in /etc/haproxy/haproxy.cfg and consists of sections:

  • global — process parameters: logging, user, connection limit;
  • defaults — default values for other sections: mode, timeouts;
  • frontend — where HAProxy accepts connections: address, port, certificate, rules;
  • backend — a group of servers, load-balancing algorithm and health checks;
  • listen — a shorthand combining frontend and backend.

Operation mode is set by the mode directive: http — HAProxy parses HTTP requests and can make decisions by path, headers and cookies; tcp — passes the connection intact, not looking inside, which is suitable for any protocols over TCP.

Example: HTTPS site on three servers

haproxy
global
    log /dev/log local0
    maxconn 20000
    user haproxy
    group haproxy

defaults
    mode http
    log global
    option httplog
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    timeout tunnel  1h

frontend web
    bind :80
    bind :443 ssl crt /etc/haproxy/certs/example.ru.pem alpn h2,http/1.1
    http-request redirect scheme https unless { ssl_fc }
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    option forwardfor
    default_backend app

backend app
    balance roundrobin
    option httpchk
    http-check send meth GET uri /health ver HTTP/1.1 hdr Host example.ru
    http-check expect status 200
    server app1 10.0.0.11:3000 check inter 3s fall 3 rise 2
    server app2 10.0.0.12:3000 check inter 3s fall 3 rise 2
    server app3 10.0.0.13:3000 check inter 3s fall 3 rise 2 backup

Explanation:

  • in the file example.ru.pem the certificate, intermediate certificates and private key are stored together in one file;
  • alpn h2,http/1.1 enables HTTP/2 for clients;
  • option forwardfor adds the X-Forwarded-For header with the client address;
  • timeout tunnel 1h sets the lifetime of connections after switching to WebSocket; without it they would be closed by timeout client and timeout server;
  • balance roundrobin sends requests in turn. Other options: leastconn — to the server with the fewest connections (good for long-lived connections), source — the same client IP always goes to the same server.

Check and apply configuration:

bash
haproxy -c -f /etc/haproxy/haproxy.cfg && systemctl reload haproxy

Server health checks

The main difference between HAProxy and the free nginx is active health checks: HAProxy itself periodically queries each server and does not wait until requests from users hit a non-working server. In the example above a GET /health request is sent every 3 seconds. After three consecutive failed checks (fall 3) the server is excluded from load balancing, after two successful ones (rise 2) it is returned. A server marked backup receives traffic only when the main servers are unavailable.

Types of checks:

  • TCP — only establishing a connection; enabled by the check word without option httpchk;
  • HTTP — a request to a health-check URL expecting a response code or content;
  • protocol checks — built-in for MySQL, PostgreSQL, Redis, SMTP, LDAP and TLS handshake;
  • agent — a small program runs on the server and reports its load to HAProxy or requests to be temporarily removed from balancing.

HAProxy does not have ICMP (ping) checks, and ping would not show whether the application is working. The most useful is an HTTP check of a separate /health address that the application serves only when the database and other dependencies are available.

Sticky sessions (binding a user to a server)

If the application stores session in the process memory, the user must hit the same server. HAProxy can issue a cookie with the server name itself:

haproxy
backend app
    balance roundrobin
    cookie SERVERID insert indirect nocache
    server app1 10.0.0.11:3000 check cookie app1
    server app2 10.0.0.12:3000 check cookie app2

If a server becomes unavailable, the user will be redirected to another and lose the session. It’s more reliable to store sessions in a shared store and not bind users to servers.

Rate limiting: stick-tables

A stick-table is an in-memory HAProxy table that stores counters by key, for example by client IP address. Rate limits are built on top of it:

haproxy
frontend web
    bind :443 ssl crt /etc/haproxy/certs/example.ru.pem alpn h2,http/1.1
    stick-table type ipv6 size 1m expire 10m store http_req_rate(10s)
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
    default_backend app

A client that sent more than 100 requests in 10 seconds receives a 429 response. Type ipv6 stores IPv4 addresses as well. The same tables are used to count client errors, limit the number of concurrent connections and exchange counters between two HAProxy instances (the peers section).

TCP mode: load balancing PostgreSQL

haproxy
frontend postgres
    mode tcp
    bind :5432
    default_backend pg_servers

backend pg_servers
    mode tcp
    balance leastconn
    option pgsql-check user haproxy
    server pg1 10.0.0.21:5432 check
    server pg2 10.0.0.22:5432 check backup

option pgsql-check verifies that PostgreSQL responds to a connection for user haproxy (it must exist in the database). HAProxy does not know which server is currently primary and which is a read-only replica. In clusters with automatic failover, for example Patroni, checks are directed to Patroni’s HTTP interface, which responds with 200 only on the primary server.

Statistics page

haproxy
frontend stats
    mode http
    bind 127.0.0.1:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:надёжный-пароль

The page shows the state of each server, number of connections, errors and response time. Do not expose this page to the Internet: in the example it listens only on the local address and is accessible via an SSH tunnel. For monitoring systems HAProxy provides metrics in Prometheus format (http-request use-service prometheus-exporter).

Caching

HAProxy has an in-memory response cache. It is designed for small frequently requested objects, for example API responses: the total volume is limited to 4095 MB, and the size of a single object is half of that volume. For caching large files and pages with large amounts of data, nginx with a disk cache is better suited, so they are often used together.

HAProxy high availability

The load balancer removes the single point of failure among application servers, but itself becomes such a point. Usually two HAProxy instances with identical configuration are deployed and a shared virtual IP address is managed by keepalived (VRRP protocol): if the primary node stops responding, the address moves to the backup within seconds.

Management without restart

Using the runtime API socket you can view state and take servers out of service without changing the configuration. Add to the global section:

haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin

After that, for example, before updating a server you put it into maintenance mode and then return it:

bash
echo "set server app/app1 state maint" | socat stdio /run/haproxy/admin.sock
echo "set server app/app1 state ready" | socat stdio /run/haproxy/admin.sock
echo "show stat" | socat stdio /run/haproxy/admin.sock

In maintenance mode HAProxy does not send new requests to the server. This allows updating the application one server at a time without downtime for users.

Logs

HAProxy writes logs via syslog. With option httplog each line contains the client address, selected frontend, backend and server, response code, response size and timings: how long the request waited in queue, how long establishing connection to the server took, and how long the server prepared the response. These fields are convenient to find exactly where the delay occurs — in the network, in the queue, or in the application itself.

Strengths and weaknesses

Advantages:

  • active server health checks in the free version;
  • works with both HTTP and any TCP traffic;
  • detailed statistics and metrics;
  • stick-tables for rate limiting and client tracking;
  • changing server state on the fly via the Runtime API (management socket) without restart.

Disadvantages:

  • does not serve static files and does not run applications;
  • cache is limited to small in-memory objects;
  • configuration syntax and rule logic require time to learn.

Choose HAProxy when the main task is to distribute load among multiple servers and quickly remove faulty ones. For a single server hosting a website, nginx is usually sufficient.

// Contact

Need help?

Get in touch with me and I'll help solve the problem

I reply within one business day (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirm that you are not a bot.

Write and get a quick reply