Nginx is commonly used as both a reverse proxy and an HTTP load balancer. Put Nginx in front of two or more application servers and incoming requests can be distributed across the backend cluster instead of being handled by a single server.
A basic Nginx load balancer requires only two important pieces of configuration:
- An
upstreamblock that defines the backend servers. - A
proxy_passdirective that sends incoming requests to that upstream group.
How to configure Nginx as a load balancer
To configure Nginx as a load balancer, follow these basic steps:
- Identify the backend application servers.
- Define those servers in an Nginx
upstreamblock. - Create a
locationblock for incoming requests. - Use
proxy_passto route those requests to the upstream group. - Test the Nginx configuration.
- Reload Nginx.
- Send requests through Nginx and verify that multiple backend servers respond.
Define the Nginx upstream servers
Assume two application servers run locally on ports 8080 and 8090. They could be Apache Tomcat servers, Spring Boot applications, Node.js services or any other HTTP-based backend.
Place both servers in an upstream group named samplecluster:
upstream samplecluster {
server 127.0.0.1:8080;
server 127.0.0.1:8090;
}With no additional load-balancing algorithm configured, Nginx distributes requests across available servers using weighted round-robin balancing.
Configure Nginx as a reverse proxy
The upstream block identifies the backend servers, but Nginx still needs to know which incoming requests should be forwarded to them.
A location block combined with proxy_pass performs that job:
location /sample/ {
proxy_pass https://samplecluster;
}A request such as /sample/index.html can now be forwarded to one of the backend servers in the samplecluster upstream group.
Nginx is therefore performing two related jobs. It acts as a reverse proxy because clients communicate with Nginx rather than directly with the application servers, and it acts as a load balancer because it distributes requests across multiple upstream servers.
The upstream and proxy_pass directives provide the foundation of an Nginx load balancer configuration.
Complete Nginx load balancer example
A complete configuration for the example looks like this:
upstream samplecluster {
server 127.0.0.1:8080;
server 127.0.0.1:8090;
}
server {
listen 80;
listen [::]:80;
server_name _;
location /sample/ {
proxy_pass https://samplecluster;
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;
}
}The proxy_set_header directives preserve useful information about the original client request when Nginx forwards it to the backend application.
For example, X-Forwarded-For allows the application server to determine the originating client's IP address instead of seeing only the address of the Nginx reverse proxy.
Test the Nginx configuration
Before restarting or reloading Nginx, test the configuration for syntax errors:
sudo nginx -tA valid configuration produces output similar to this:
nginx: configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulOnce the configuration passes validation, reload Nginx:
sudo systemctl reload nginxA reload is generally preferable to a full restart for configuration changes because Nginx can apply the new configuration without unnecessarily interrupting active connections.
Test the Nginx load balancer
Send several requests to the Nginx server:
curl https://localhost/sample/
curl https://localhost/sample/
curl https://localhost/sample/
curl https://localhost/sample/If the two backend applications return identifying information such as their port number or hostname, the responses make it easy to see requests being distributed across the cluster.
A request handled by a backend application demonstrates that the Nginx reverse proxy is forwarding traffic correctly.
Weighted Nginx load balancing
Backend servers do not always have identical capacity. One server might have twice the CPU or memory of another and therefore be capable of processing more traffic.
Nginx lets you assign each upstream server a weight:
upstream samplecluster {
server 127.0.0.1:8080 weight=2;
server 127.0.0.1:8090 weight=1;
}In this configuration, the server on port 8080 receives approximately twice the request allocation of the server on port 8090 over time.
IP-based session persistence with ip_hash
Some applications benefit when requests from the same client are consistently routed to the same backend server.
Nginx provides the ip_hash load-balancing method for IP-based persistence:
upstream samplecluster {
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8090;
}With ip_hash enabled, Nginx uses information from the client's IP address to consistently select an upstream server.
This is often described as a form of session persistence, although it should not be confused with application-level or cookie-based sticky sessions. Multiple users behind the same proxy or NAT gateway may appear to Nginx under the same public IP address.
Temporarily disable an upstream server
If a backend server needs maintenance, it can remain in the configuration while being explicitly marked down:
upstream samplecluster {
server 127.0.0.1:8080;
server 127.0.0.1:8090;
server 127.0.0.1:8070 down;
}Nginx will not send requests to the server marked down. Remove the parameter and reload the configuration when the server is ready to receive traffic again.
Basic Nginx passive failure handling
You can also tell Nginx to stop selecting a backend temporarily after repeated communication failures:
upstream samplecluster {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8090 max_fails=3 fail_timeout=30s;
}These settings provide basic passive failure handling. Nginx observes failures encountered while processing real client requests and temporarily treats a server as unavailable when the configured failure conditions are met.
Nginx load balancer configuration summary
For many applications, an Nginx load balancer requires surprisingly little configuration. Define the backend servers in an upstream block, route requests to that group with proxy_pass, validate the configuration and reload Nginx.
From there, Nginx can support more advanced routing requirements such as weighted servers, IP-based persistence and passive failure handling.
The result is a simple architecture in which clients communicate with one public Nginx endpoint while Nginx distributes the actual workload across multiple application servers behind it.