Nginx Reverse Proxy: Request Routing and Load Distribution Techniques

Proxy Server Fundamentals

Proxy servers act as intermediaries between clients and backend services. In Nginx, reverse proxy configuration enables request forwarding, response buffering, and header manipulation. This guide covers practical implementations for routing traffic to HTTP and non-HTTP upstream servers.

Basic Request Forwarding Configuration

When Nginx receives a request and forwards it to a proxied server, it retrieves the response and transmits it back to the client. The proxy_pass directive handles this behavior within a location block.

worker_processes 4;

events {
    worker_connections 1024;
}

http {
    upstream application_servers {
        server 10.0.0.10:8080;
        server 10.0.0.11:8081;
    }

    server {
        listen 80;
        server_name example.com;
        
        location /api/ {
            proxy_pass http://application_servers;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

The configuration routes all requests matching /api/ to the upstream cluster. Headers like Host and client IP are forwarded using proxy_set_header.

Routing to Multiple Backend Servers

The upstream directive groups multiple backend servers into a single logical unit. Unlike multiple proxy_pass statements, upstream blocks define a unified endpoint for load distribution.

worker_processes 4;

events {
    worker_connections 1024;
}

http {
    upstream backend_pool {
        server 10.0.0.10:9000;
        server 10.0.0.11:9001;
        server 10.0.0.12:9002;
    }

    server {
        listen 8888;
        location / {
            proxy_pass http://backend_pool;
        }
    }
}

Each server in the upstream block receives requests in rotation unless customized with additional parameters.

Weighted Load Balancing

Assign weights to backend servers using the weight parameter. Higher values direct proportionally more traffic to that server.

worker_processes 4;

events {
    worker_connections 1024;
}

http {
    upstream backend_pool {
        server 10.0.0.10:9000 weight=3;
        server 10.0.0.11:9001 weight=2;
        server 127.0.0.1:9002 weight=1;
    }

    server {
        listen 8888;
        location / {
            proxy_pass http://backend_pool;
        }
    }
}

In this example, the first server handles 50% of requests, the second handles rough 33%, and the local instance handles the remaining 17%. The local server configuration demonstrates that Nginx can proxy to the same machine running other services.

Configuring Health Checks

Add backup and down flags to manage server availability:

upstream backend_pool {
    server 10.0.0.10:9000 weight=3;
    server 10.0.0.11:9001 weight=2;
    server 10.0.0.12:9002 backup;
}

Backup servers only receive traffic when all primary servers are unavailable.

Organizing Configuration Files

Split large configurations into modular files using the include directive:

# main.conf
worker_processes 4;

events {
    worker_connections 1024;
}

http {
    upstream backend_pool {
        server 10.0.0.10:9000 weight=3;
        server 10.0.0.11:9001 weight=2;
        server 127.0.0.1:9002 weight=1;
    }

    include /etc/nginx/conf.d/*.conf;
}
# app_server.conf
server {
    listen 8888;
    location / {
        proxy_pass http://backend_pool;
    }
    location /blog/ {
        proxy_pass http://10.0.0.20:8000;
    }
}
# status_server.conf
server {
    listen 8889;
}

Each server block resides in a separate file, improving maintainability for complex deployments.

Understanding HTTP and Server Block Relationships

In Nginx configuration, the http context can contain multiple server blocks, and each server block can listen on multiple ports. This creates an N:M relationship where any server block can bind to any port.

http {
    # First virtual host configuration
    server {
        listen 80;
        server_name site1.example.com;
        location / {
            proxy_pass http://backend_pool;
        }
    }

    # Second virtual host configuration
    server {
        listen 80;
        server_name site2.example.com;
        location / {
            proxy_pass http://alternate_backend;
        }
    }
}

Nginx matches incoming requests to the appropriaet server block based on the listen directive and server_name matching logic.

Leveraging Nginx Core Data Structures

Nginx provides reusable C components that can be incorporated into custom modules or standalone applications. Below is an example demonstrating ngx_str_t usage:

#include <stdio.h>
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"

int main()
{
    ngx_str_t greeting = ngx_string("Hello World!");
    printf("String length: %ld\n", greeting.len);
    printf("String value: %s\n", greeting.data);
    return 0;
}

Compilation requires specifying include paths:

gcc -o ngx_str_demo ngx_str_demo.c \
    -I /path/to/nginx-1.13.7/src/core/ \
    -I /path/to/nginx-1.13.7/src/event/ \
    -I /path/to/nginx-1.13.7/src/os/unix/ \
    -I /path/to/nginx-1.13.7/objs/ \
    -I /path/to/pcre-8.41/ \
    -I /path/to/openssl-1.1.0g/include/

The compiled binary outputs:

string length: 12
string: Hello World!

Automate builds using Makefile:

CC = gcc
LD = gcc

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

INCLUDE = -I /path/to/nginx-1.13.7/src/core/ \
          -I /path/to/nginx-1.13.7/src/event/ \
          -I /path/to/nginx-1.13.7/src/os/unix/ \
          -I /path/to/nginx-1.13.7/objs/ \
          -I /path/to/pcre-8.41/ \
          -I /path/to/openssl-1.1.0g/include/

TARGET = ngx_str_demo

.PHONY: all clean

all: $(TARGET)

$(TARGET): $(OBJS)
    $(LD) -o $@ $^

%.o: %.c
    $(CC) -c $< $(INCLUDE)

clean:
    rm -f $(OBJS) $(TARGET)

Additional Nginx Components

Nginx offers numerous foundational components suitable for module development:

  • Data structures: lists, queues, hash tables, dynamic arrays, red-black trees
  • Memory management: memory pools, memory allocators
  • Synchronization: atomic operations, shared memory segments, thread pools
  • Protocol handling: HTTP filter and handler modules, upstream modules
  • Logging: configurable log levels and output destinations

These components provide buildding blocks for extending Nginx functionality or developing standalone applications that benefit from Nginx's battle-tested infrastructure.

Tags: nginx reverse-proxy load-balancing upstream configuration

Posted on Mon, 17 Aug 2026 16:39:00 +0000 by keeve