All Articles

Nginx vs HAProxy — Which Load Balancer to Use in 2026?

Nginx and HAProxy both handle load balancing — but they work differently and excel at different things. Honest comparison of performance, configuration, health checks, observability, and when to pick each in 2026.

DevOpsBoysApr 25, 20266 min read
Share:Tweet

You need a load balancer. Nginx and HAProxy are the two most deployed open-source options. Both are battle-tested, both run at massive scale — but they make different trade-offs.

Here's the honest comparison.


What Each Tool Actually Is

Nginx started as a web server and added load balancing. In 2026 it's used as:

  • Reverse proxy / load balancer
  • Static file server
  • API gateway
  • TLS termination point
  • Kubernetes ingress controller (via ingress-nginx)

HAProxy was built from day one as a load balancer and proxy. It does one thing and does it exceptionally well:

  • TCP and HTTP load balancing
  • Health checking
  • SSL termination
  • Advanced traffic routing

HAProxy does not serve static files. It is not a web server. It is purely a proxy.


Configuration Style

Both use text-based config files, but the style differs significantly.

Nginx:

nginx
# nginx.conf
upstream backend {
    least_conn;
    
    server app1.internal:8080 weight=3;
    server app2.internal:8080 weight=2;
    server app3.internal:8080 weight=1;
    
    keepalive 32;
}
 
server {
    listen 80;
    server_name api.yourdomain.com;
    
    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
    
    location /health {
        return 200 "ok";
    }
}

HAProxy:

haproxy
# haproxy.cfg
global
    log stdout format raw local0
    maxconn 50000
 
defaults
    mode http
    log global
    option httplog
    option forwardfor
    option http-server-close
    timeout connect 5s
    timeout client  60s
    timeout server  60s
 
frontend api_frontend
    bind *:80
    default_backend api_backend
 
backend api_backend
    balance leastconn
    option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
    
    server app1 app1.internal:8080 weight 3 check inter 5s rise 2 fall 3
    server app2 app2.internal:8080 weight 2 check inter 5s rise 2 fall 3
    server app3 app3.internal:8080 weight 1 check inter 5s rise 2 fall 3

HAProxy's config is more explicit and purpose-built. Nginx is more familiar to web developers.


Load Balancing Algorithms

AlgorithmNginxHAProxy
Round Robin
Least Connectionsleast_connleastconn
IP Haship_hashsource
Weightedweight=Nweight N
Random✅ (Nginx Plus)random
URI Hashuri
Least Response Time✅ (Nginx Plus only)leastconn approximation
First Availablefirst
Random with 2 choicesrandom(2) (power of two)

HAProxy has more built-in algorithms. Nginx's advanced algorithms require Nginx Plus (paid).


Health Checks

This is where HAProxy significantly outperforms Nginx open-source.

Nginx (open-source) — passive health checks only:

nginx
upstream backend {
    server app1.internal:8080;
    server app2.internal:8080;
    
    # Passive: marks server down after N failed requests
    # No active probing in open-source Nginx
}

Nginx open-source cannot actively probe backends. It only marks them down after receiving errors from real requests.

HAProxy — active health checks:

haproxy
backend api_backend
    # Active health check: sends HTTP GET /health every 5 seconds
    option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
    
    server app1 app1.internal:8080 check inter 5s rise 2 fall 3
    # inter 5s  = check every 5 seconds
    # rise 2    = mark healthy after 2 consecutive successes
    # fall 3    = mark unhealthy after 3 consecutive failures

HAProxy actively polls each backend. You know within 5 seconds if a backend goes down, before any real traffic hits it.

Nginx Plus (paid, ~$3,500/year/instance) adds active health checks. The open-source version does not have them.


Performance Benchmarks (2026)

Both are extremely fast. At typical application-server scales, you'll never hit the limit of either.

For pure proxy throughput on modern hardware:

MetricNginxHAProxy
Requests/sec (HTTP, 64 byte)~300K~400K
Concurrent connections~50K (worker-limited)~500K+
Memory per connectionHigherLower
Latency (p99)SimilarSlightly lower

HAProxy handles more concurrent connections with less memory. This matters for long-lived connections (WebSocket, Server-Sent Events) or very high connection counts.

For most applications (< 10K concurrent connections), the difference is irrelevant.


Observability

HAProxy Stats Page:

HAProxy has a built-in stats dashboard — no extra tools needed:

haproxy
frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:password

Visit http://your-haproxy:8404/stats for a real-time table showing:

  • Requests/sec per backend
  • Response time
  • Error rates
  • Active connections
  • Server status (UP/DOWN)

HAProxy Prometheus Exporter:

bash
# HAProxy 2.0+ has built-in Prometheus metrics
frontend prometheus
    bind *:8405
    http-request use-service prometheus-exporter if { path /metrics }

Nginx:

nginx
# Basic stub_status (minimal metrics)
location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

For proper Nginx metrics, you need nginx-prometheus-exporter (separate process) or Nginx Plus.

HAProxy's built-in observability is significantly better out of the box.


Dynamic Configuration (Without Restart)

Nginx: Requires nginx -s reload to apply config changes. Graceful reload (no dropped connections) but still a process signal.

HAProxy: Has a Runtime API for zero-downtime changes:

bash
# Add a server dynamically without restart
echo "add server backend/app4 app4.internal:8080" | \
  socat stdio /var/run/haproxy/admin.sock
 
# Drain a server (stop new connections, finish existing)
echo "set server backend/app1 state drain" | \
  socat stdio /var/run/haproxy/admin.sock
 
# Take a server out
echo "set server backend/app1 state maint" | \
  socat stdio /var/run/haproxy/admin.sock

HAProxy's runtime API is extremely powerful for blue-green deployments, rolling updates, and dynamic scaling.


TCP Load Balancing

HAProxy was built for both HTTP and TCP. TCP load balancing is first-class:

haproxy
frontend mysql_frontend
    bind *:3306
    mode tcp
    default_backend mysql_backend
 
backend mysql_backend
    mode tcp
    balance leastconn
    option mysql-check user haproxy_check
    
    server mysql1 mysql1.internal:3306 check
    server mysql2 mysql2.internal:3306 check backup

HAProxy can load balance MySQL, PostgreSQL, Redis, SMTP, and any other TCP protocol — including custom health checks specific to each protocol.

Nginx supports TCP/UDP load balancing via the stream module:

nginx
stream {
    upstream mysql_backend {
        server mysql1.internal:3306;
        server mysql2.internal:3306;
    }
    
    server {
        listen 3306;
        proxy_pass mysql_backend;
    }
}

Both work for TCP. HAProxy has more protocol-aware health checks.


When to Choose Each

Choose Nginx when:

  • You also need to serve static files or act as a web server
  • You're deploying in Kubernetes (ingress-nginx is the most mature ingress controller)
  • Your team already knows Nginx
  • You need a reverse proxy + load balancer in one for a simpler setup
  • You're running at < 5K concurrent connections

Choose HAProxy when:

  • You need active health checks (critical for zero-downtime deploys)
  • You have high concurrent connection counts (10K+)
  • You need TCP load balancing with protocol-aware health checks
  • You want the Runtime API for dynamic server management
  • You need detailed built-in observability without extra tools
  • You're building infrastructure for databases, queues, or custom protocols

Using Both Together

Many high-scale setups use both:

Internet → HAProxy (L4 TCP load balancing, SSL termination)
               → Nginx (reverse proxy, static files, application routing)
                   → Application servers

HAProxy handles the raw TCP layer (connection management, SSL, high concurrency) and Nginx handles application-layer concerns (URL routing, caching, static assets).


Quick Start

Nginx:

bash
apt install nginx
# Edit /etc/nginx/nginx.conf
nginx -t        # Test config
systemctl reload nginx

HAProxy:

bash
apt install haproxy
# Edit /etc/haproxy/haproxy.cfg
haproxy -c -f /etc/haproxy/haproxy.cfg   # Validate config
systemctl reload haproxy

For learning: Nginx Fundamentals on Udemy covers proxy and load balancing config end-to-end. HAProxy's official documentation is unusually good — worth reading directly.

The honest recommendation: If you're on Kubernetes, use ingress-nginx. If you're running bare metal or VMs and care about active health checks and high connection counts, use HAProxy. If you're building a simple setup and your team knows Nginx, Nginx is fine.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments