Has your site started slowing down when traffic spikes? You added more servers, but requests pile up on one while others sit idle. That’s a classic symptom of missing or misconfigured load balancing.
We, at Meteora Web, have been managing servers for years — including those that keep e‑commerce platforms alive during flash sales. When a client tells us “the site gets sluggish after 10 AM,” load balancing is the first thing we check. And often we find the wrong algorithm for the application.
This guide covers the three most common Nginx load balancing methods: round robin, least connections and ip‑hash. We won’t just show you the syntax — we’ll tell you when to use each one, why they work, and which mistakes to avoid so you don’t end up with overloaded backends and lost customers.
Round Robin, Least Connections and IP-Hash: How They Work and When to Use Each
Let’s start with the basics. Nginx supports several load‑balancing algorithms. The three you need for 90% of cases are:
- Round Robin: distributes requests sequentially, one after another, to each server in the pool.
- Least Connections: sends the request to the server with the fewest active connections at that moment.
- IP-Hash: maps each client IP to a specific server using a hash function.
Your choice depends on the application. A simple static blog works fine with round robin. A web app storing session data locally (PHP file‑based sessions) needs ip‑hash or a shared backend. An API with uneven request processing times benefits from least connections.
Sponsored Protocol
We’ve seen projects where round robin on servers of different capacities caused bottlenecks: the powerful server got saturated because it was just its turn. That’s a rookie mistake, but it happens more often than you’d think.
Round Robin: The Default You Should Not Underestimate
It’s Nginx’s default. No special directive needed — just declare an upstream block and you get round robin. Minimal example:
upstream backend {
server web1.example.com;
server web2.example.com;
server web3.example.com;
}
Works well when all servers have identical capacity and request response times are similar. Downside: it ignores current load. If a request to web1 takes 10 seconds (because it does heavy processing), the next request still goes to web1 even if it’s already busy.
When to use it: homogeneous loads, identical servers, stateless applications (REST APIs, file servers, CDN proxies).
Least Connections: Smart, But Not Always the Right Fit
Nginx tracks how many connections are active on each server and sends the next request to the least loaded one. Ideal for applications with variable workloads.
Sponsored Protocol
upstream backend {
least_conn;
server web1.example.com;
server web2.example.com;
server web3.example.com;
}
Caution: active connections ≠ CPU or memory load. A server can have few connections but each one very heavy. In practice, for most PHP/Node/Python web apps, least_conn outperforms round robin when response times are uneven.
Common mistake: pairing least_conn with aggressive keepalive. If clients keep persistent connections (HTTP/1.1 or HTTP/2), the active‑connection count stays high even on lightly loaded servers. We recommend monitoring actual connection numbers and tuning keepalive carefully.
IP-Hash: When Session Persistence Is Non‑Negotiable
IP‑hash ensures the same client (same source IP) always hits the same backend. Essential for applications that store session state locally (PHP files) without a central store like Redis.
upstream backend {
ip_hash;
server web1.example.com;
server web2.example.com;
server web3.example.com;
}
Nginx hashes the client IP and maps it to a server. If a server is removed (marked down), its requests are redistributed, but the hash stays stable for the remaining ones. Note: behind a proxy or CDN, the client IP may be the proxy’s — use hash $http_x_forwarded_for or a session cookie instead.
Sponsored Protocol
When to use it: only if you cannot centralize sessions. We have clients still relying on local PHP sessions. For them, ip‑hash is a pragmatic fix, but the real answer is own your stack and move sessions to Redis, removing IP dependency.
How to Configure Nginx Load Balancing with Weights and Backup Servers
Each method can be combined with weight for non‑uniform distribution and backup servers that only kick in when primaries are down.
upstream backend {
least_conn;
server web1.example.com weight=3;
server web2.example.com weight=1;
server web3.example.com backup;
}
Here web1 gets three times more traffic than web2 (because it’s more powerful), and web3 stays as a fallback. Backup servers are crucial for high availability without paying for always‑on hardware. We use this pattern extensively.
Which Algorithm Should You Pick for an E‑commerce Store or a PHP App?
It comes down to session handling. If you’ve already centralised sessions on Redis (and you should), least_conn is best for variable loads. If you’re still on file‑based sessions, ip_hash is mandatory — but plan the migration to a shared backend.
Sponsored Protocol
A frequent error: using round robin with servers of different capacities and no weights. The weaker server saturates, the stronger one underutilises. Add weights immediately.
For stateless REST APIs (JWT, no server‑side session), round robin is simple and perfect. For WebSocket applications, use ip_hash or sticky sessions via cookies.
Common Nginx Load Balancing Mistakes and How to Avoid Them
- Not setting
max_failsandfail_timeout: if a backend goes down, Nginx keeps sending requests there, making them fail. Setmax_fails=3 fail_timeout=30s. - Forgetting
proxy_next_upstream: if the backend returns a 500 error, Nginx won’t retry another server unless you tell it to. - Using
keepalivewithout considering active connections: with least_conn, keepalive connections are counted and can skew distribution. - Not testing failover: shut down one server and verify requests are redirected without timeouts. We do this during every deployment.
upstream backend {
least_conn;
server web1.example.com max_fails=3 fail_timeout=30s;
server web2.example.com max_fails=3 fail_timeout=30s;
server web3.example.com backup;
}
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
What to Do Now
- Review your current upstream configuration: if you lack
least_connorip_hash, decide whether a change is needed. Homogeneous loads → round robin; variable loads → least_conn; local sessions → ip_hash. - Add weights and a backup server: use
weightfor servers with different capacities. Add a backup for resilience. - Centralise your sessions: if you rely on ip_hash only for session persistence, plan a migration to Redis. Read our pillar guide on Nginx for a full production setup.
- Test failover: shut down one backend and verify traffic shifts correctly. Use
curlor tools likeabfor load simulation. - Monitor active connections: use
nginx statusor Prometheus to track per‑server connections. If they stay unbalanced, revisit your configuration.
Load balancing isn’t just syntax — it’s an architectural decision that directly affects site speed and user satisfaction. At Meteora Web, we treat it with the same rigour we apply to a client’s balance sheet: every choice has a cost and a return. Ready to optimise yours?
Sponsored Protocol
External resource: Official Nginx load‑balancing documentation.