Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To load-balance an HTTP application with NGINX, create an upstream group listing your backend servers, then send requests from a public location to that group with proxy_pass. Round robin is the default distribution method. Choose least_conn for uneven request durations, ip_hash when temporary client affinity is necessary, or a hash key for cache-oriented routing. Open-source NGINX detects failures passively; periodic active health checks require NGINX Plus.

How NGINX load balancing works

NGINX sits between clients and your application servers as a reverse proxy. Clients connect to one public address, while NGINX selects a server from an internal upstream group for each request. The backends can be separate virtual machines, containers, physical hosts or ports on the same network.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reusable upstream name is the centre of the configuration. Your public server block accepts the request, and its location block passes the request to that upstream name rather than to one fixed host. NGINX then applies the selected balancing method and failure rules.

Before you configure NGINX

  • Install NGINX on a host that can reach every backend address and port over the required protocol.
  • Confirm that each backend serves the same application contract: paths, authentication behavior, response format and any required static assets.
  • Decide whether the service is HTTP/HTTPS or a raw TCP/UDP protocol. HTTP uses the http context and proxy_pass; TCP and UDP use the stream context.
  • Identify a lightweight health endpoint, such as /healthz, only if your application implements it and it checks the dependencies that matter.
  • Document session and connection state. A local in-process session, shopping cart, upload, WebSocket or other stateful connection may need a shared store or affinity.

Basic HTTP configuration

The following pattern distributes requests among three application servers. Replace the names, ports and public listener with values from your network.

http {
    upstream app_backend {
        server app1.example.com:8080;
        server app2.example.com:8080;
        server app3.example.com:8080;
    }

    server {
        listen 80;
        server_name app.example.com;

        location / {
            proxy_pass http://app_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;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Put the upstream definition inside the http context. The public server block can be in the main configuration or an included site file, provided both are ultimately loaded in the same NGINX configuration. The proxy-header lines are a conventional starting point; confirm the names and trust model expected by your framework, authentication layer and access logs.

What each directive does

  • upstream app_backend gives the backend group a name that can be reused by one or more locations.
  • Each server line identifies a backend host and port. NGINX chooses among these entries.
  • proxy_pass http://app_backend hands the request to the group instead of a single origin.
  • Host, X-Real-IP, X-Forwarded-For and X-Forwarded-Proto preserve information applications commonly need for URL generation, logging and client-IP handling.

Choose the balancing method

Start with the workload and state model, not with a preference for a particular directive. The methods below solve different problems.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Method Configuration Best fit Important limitation Edition
Round robin No method directive (default for HTTP) Backends have similar capacity and requests are broadly comparable Does not account for current connection count or latency NGINX Open Source and NGINX Plus
Least connections least_conn; Requests have different durations or long-lived connections At very low load, per-worker counters can make selection less precise without a shared zone NGINX Open Source and NGINX Plus
IP hash ip_hash; Temporary client-IP affinity Affinity is not shared application state; clients behind one NAT address may converge on one backend NGINX Open Source and NGINX Plus
Generic consistent hash hash $request_uri consistent; Cache-like workloads where a chosen key should map repeatedly to the same server Choosing the wrong key creates hot spots and does not provide user-session semantics NGINX Open Source and NGINX Plus
Least time NGINX Plus method Selection based on latency together with active connections Commercial-only capability NGINX Plus
Random NGINX Plus method Randomized selection, including Plus variants that consider load Commercial-only capability NGINX Plus

Round robin: the safe starting point

Round robin sends requests evenly while taking configured server weights into account. It is usually appropriate when the servers have comparable CPU, memory and network capacity and one request costs roughly as much as another.

upstream app_backend {
    server app1.example.com:8080 weight=2;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

With a weight of 2 versus the default weight of 1, the first server receives approximately twice the share over a sufficiently large request sample. It is a relative share, not a guarantee for every short sequence of requests.

Least connections for variable request length

upstream app_backend {
    least_conn;
    server app1.example.com:8080;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

NGINX selects the server with the fewest active connections, considering weights. This can reduce queueing when one request may take milliseconds and another may hold a connection for minutes. For low-volume traffic, use a shared upstream zone so workers coordinate their counters rather than making decisions from isolated per-worker views.

IP hash for client affinity

upstream app_backend {
    ip_hash;
    server app1.example.com:8080;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

IP hashing derives the selection from the client IP and keeps that client on the same available server. It is useful when you cannot immediately move session data out of a process, but it is not a substitute for shared state. A user whose address changes can move to another backend, and many users sharing a proxy or carrier-grade NAT may overload one server. If the selected server is unavailable, NGINX can select another according to its failure behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Consistent hashing for cache-oriented routing

upstream cache_backend {
    hash $request_uri consistent;
    server cache1.example.com:8080;
    server cache2.example.com:8080;
    server cache3.example.com:8080;
}

This example uses the URI as the key. Consistent hashing limits how many keys move when a backend is added or removed, which is useful when each server holds a local cache. Select a key that reflects the data locality you actually need; a URI-only key is rarely appropriate for authenticated user sessions.

Use weights deliberately

Weights let a stronger or less-loaded server receive a larger relative share under round robin, least connections and other methods that account for weights. Keep the ratio proportional to sustainable capacity rather than merely to the number of CPU cores. Recheck the ratio after changing instance types, connection limits or database capacity.

For a gradual rollout, give a new backend a lower weight, observe errors and latency, then increase it in stages. This is traffic shaping, not a replacement for application-level canary controls: requests can still differ in size and duration.

Configure passive failure handling

Open-source NGINX and NGINX Plus both use passive health behavior based on real client transactions. When a backend repeatedly fails according to its configured failure parameters, NGINX can mark it unavailable temporarily and stop sending requests to it. A subsequent recovery period lets NGINX try the server again.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
upstream app_backend {
    server app1.example.com:8080 max_fails=3 fail_timeout=30s;
    server app2.example.com:8080 max_fails=3 fail_timeout=30s;
    server app3.example.com:8080 max_fails=3 fail_timeout=30s;
}

Treat these values as an example to tune, not universal defaults. A short timeout can eject a server during a transient network pause; a long timeout can leave a genuinely broken server out of rotation for too long. Align the thresholds with your request timeout, retry policy and normal response-time distribution.

Active health checks with NGINX Plus

Passive checks only learn about a backend when traffic reaches it. NGINX Plus adds periodic active HTTP checks that send a health-check request and evaluate the response, allowing an idle or newly added server to be tested before ordinary traffic is assigned. The commercial health-check module requires the upstream group to reside in shared memory.

Use a dedicated endpoint such as /healthz only when the application defines it. A useful endpoint verifies the dependencies that must work for a real request—such as a database or queue—without performing an expensive business operation. A process that merely accepts a TCP connection is not proof that the application is healthy.

The exact active-check directive and response criteria depend on your NGINX Plus configuration. Define the expected status, interval and failure threshold to match your service-level objective, and ensure the endpoint is not exposed in a way that leaks operational details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Share upstream state with a zone

upstream app_backend {
    zone app_backend 64k;
    least_conn;
    server app1.example.com:8080;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

An upstream zone places the server-group configuration and related counters in shared memory so worker processes can coordinate. Without a zone, each worker keeps its own copy. The difference matters most for accurate least-connections decisions at low load, and a zone is also required for dynamic upstream configuration scenarios. Size the zone for the amount of upstream metadata your deployment needs and validate the configuration on your installed NGINX version.

Sessions, logins and other state

Round robin and least connections can send consecutive requests from one browser to different backends. If a session is stored only in a local process, the second request may appear unauthenticated or lose a cart. The durable solution is to put session data in a shared store or use another application-level state strategy.

Use ip_hash only when its client-IP affinity trade-offs are acceptable. It does not replicate files, in-memory caches or database transactions between servers. NGINX Plus documents additional sticky-session persistence for deployments that need a different persistence model; evaluate that against a shared session store rather than assuming stickiness fixes every state problem.

HTTPS and forwarding details

Terminate TLS at NGINX when it is the intended trust boundary, then proxy to HTTP or HTTPS backends according to your security policy. Preserve the original scheme with X-Forwarded-Proto so the application can generate secure redirects. If encryption must continue to each backend, use an HTTPS proxy_pass and configure certificate verification and backend trust explicitly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the application trust forwarded headers only from your NGINX address range. Otherwise a client could submit its own X-Forwarded-For value and corrupt audit logs or access-control decisions.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Load balancing TCP and UDP services

HTTP directives do not proxy arbitrary database, messaging or game protocols. Put raw TCP or UDP upstreams in the stream context:

stream {
    upstream tcp_service {
        server service1.example.com:9000;
        server service2.example.com:9000;
    }

    server {
        listen 9000;
        proxy_pass tcp_service;
    }
}

For stream upstreams, weighted round robin is the default. NGINX also documents least connections, least time and hash selection for stream traffic. Select a method based on connection duration and protocol semantics; an HTTP URI hash cannot be used when there is no HTTP request to inspect.

Validate and roll out the configuration

  1. Check reachability. From the NGINX host, connect to every backend address and port and request the expected application path. A successful DNS lookup alone does not prove that a firewall, security group or service listener permits traffic.
  2. Check each backend directly. Verify status codes, response bodies, authentication behavior and the health endpoint independently before introducing the proxy.
  3. Test syntax. Run nginx -t against the configuration that the running service will load. Fix every reported error before reloading.
  4. Reload safely. Use your service manager’s reload operation so existing connections can finish while workers adopt the validated configuration. Confirm the reload completed successfully.
  5. Exercise distribution. Send repeated requests and inspect access and upstream logs. Include a response marker or backend identifier in a non-production environment if you need to see which server answered.
  6. Simulate a failure. Stop or isolate one backend and verify the configured passive behavior, or the NGINX Plus active check, removes it from service and later allows recovery.
  7. Test stateful flows. Log in, refresh, submit a cart or hold a WebSocket long enough to reveal whether the chosen method and application state model work together.
  8. Monitor after rollout. Watch upstream response time, connection counts, failure counts and 5xx responses, not just NGINX’s own CPU and memory.

Troubleshooting common failures

Symptom Likely cause What to check
502 Bad Gateway immediately after reload Backend port is closed, unreachable or speaking the wrong protocol Connect from the NGINX host to the exact host and port; inspect the error log and firewall rules.
Only one backend receives traffic Another server is failing, has a much lower weight, or the test sample is too small Review upstream failure messages, weights and a larger set of requests.
Users lose login state intermittently Local sessions with round robin or least connections Move sessions to shared storage or deliberately use an affinity strategy, then test client-IP and proxy behavior.
Traffic concentrates behind one office or carrier ip_hash maps many users sharing one public address to one backend Measure source-address distribution and prefer shared state with a non-affinity method when possible.
Least connections appears uneven at low traffic Workers are using separate counters Configure an upstream zone and verify that the zone is loaded by the active configuration.
Health endpoint reports green while users receive errors The endpoint checks only process liveness, not required dependencies Make the endpoint test the dependencies that define application readiness, without exposing secrets.
Redirects point to HTTP or the wrong host Forwarded scheme or host headers are missing or untrusted Set the headers expected by the framework and configure its trusted-proxy settings.
TCP service fails with HTTP directives Protocol configured in the wrong context Move the listener and upstream to stream and use stream-compatible proxy settings.

Open Source or NGINX Plus?

Capability NGINX Open Source NGINX Plus
HTTP upstream load balancing Yes Yes
Round robin, weights, least connections, IP hash and generic hash Yes Yes
Passive failure handling Yes Yes
Periodic active HTTP health checks Not included Yes
Least-time and Random methods Not included Yes
Documented additional sticky-session persistence Not included Yes
Dynamic upstream behavior that relies on shared state Capabilities depend on the installed version and configuration Supported commercial feature set

For a small, stable pool, open-source NGINX usually provides the core proxying, balancing and passive failure behavior. NGINX Plus becomes relevant when you need proactive checks before live traffic, commercial persistence features, least-time or Random selection, or supported dynamic upstream operations. Confirm feature availability against the edition and version you operate.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Design decisions to record

  • Protocol: HTTP/HTTPS belongs in http; TCP/UDP belongs in stream.
  • Objective: choose even distribution, active-connection balancing, latency-aware selection or affinity explicitly.
  • Failure detection: decide whether passive transaction failures are sufficient or whether periodic active checks justify NGINX Plus.
  • State: prefer shared application state; treat affinity as a compatibility measure with measurable trade-offs.
  • Change model: static upstream edits require validated reloads, while shared-zone and edition-specific dynamic features change how membership is managed.

A maintainable deployment keeps the upstream group small and clearly named, tests every backend directly, validates every reload, and measures the behavior that matters to users: successful responses, latency, connection duration and recovery after failure.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.