How does NGINX work as a reverse proxy and load balancer?
Quick Answer
NGINX sits between clients and backend servers, forwarding requests and returning responses. As a load balancer, it spreads traffic across multiple backends using algorithms like round-robin, least connections, or IP hash to improve availability.
Detailed Answer
Think of NGINX as a receptionist at a busy hospital. When a patient walks in, the receptionist doesn't treat them directly. Instead, she checks which doctor is free, sends the patient there, and passes the doctor's response back. The patient never needs to know which doctor handled their case. This is how a reverse proxy works -- it sits between clients and servers, hiding the backend details from the outside world.
The key directive that makes reverse proxying work is proxy_pass. When a request comes in, NGINX checks its location blocks to match the URL, then forwards the request to backend servers listed in an upstream block. The upstream block groups your servers together and picks a load-balancing strategy. Round-robin is the default, handing out requests evenly. Least-connections sends traffic to whichever server is least busy, which works well for slow or long-running requests. IP-hash always sends the same client to the same server based on their IP, which helps when you need session stickiness.
Under the hood, NGINX uses an event-driven, non-blocking design. On Linux it uses epoll; on BSD it uses kqueue. This means a single worker process can juggle thousands of connections at once without creating a new thread for each one. When proxying, NGINX keeps separate connections open to the client and the backend. It reads the client's request into a buffer, sends it upstream, gets the response, and streams it back. You can use keepalive directives to reuse connections to your backends, which cuts out the overhead of opening new TCP connections for every request and makes a big difference under heavy traffic.
In real-world setups, NGINX is the standard front door for microservices. You typically run it on dedicated machines or as a Kubernetes Ingress controller. TLS is terminated at the NGINX layer, so backends only deal with plain HTTP. Health checks let NGINX stop sending traffic to servers that are down. NGINX Plus has active health checks, while the open-source version uses passive checks through max_fails and fail_timeout. You should also set proxy headers like X-Real-IP and X-Forwarded-For so your apps know the real client IP instead of seeing the proxy's address.
A common mistake is forgetting to set proxy_set_header Host $host. Without it, your backend gets the upstream server name instead of the original hostname, which breaks virtual-host routing. Another pitfall is not tuning proxy_buffer_size and proxy_buffers -- if a backend response is bigger than the default buffers, you get 502 errors. Finally, when using least_conn with WebSocket connections, keep in mind that long-lived connections throw off the count and may prevent new connections from being distributed evenly.