Understanding nginx: Web Server, Reverse Proxy, and Load Balancer Explained with Docker Compose


Modern web applications need to handle thousands of concurrent users while maintaining high performance and reliability. This is where nginx shines as one of the most powerful and versatile web servers available today. In this comprehensive guide, we'll explore how nginx functions as a web server, reverse proxy, and load balancer through a hands-on Docker example.
GitHub Repository: https://github.com/sachindumalshan/simple-nginx-app.git
What is nginx?
nginx (pronounced "engine-x") is a high-performance web server, reverse proxy server, and load balancer. Nginx has become one of the most popular web servers in the world, powering over 30% of all websites globally.
Key Features of Nginx:
High Performance: Can handle thousands of concurrent connections with low memory usage
Reverse Proxy: Routes client requests to backend servers
Load Balancing: Distributes incoming requests across multiple servers
Static Content Serving: Efficiently serves static files like HTML, CSS, and images
SSL/TLS Termination: Handles encryption and decryption
Caching: Improves performance by storing frequently requested content
Demo Application Architecture
Before diving into concepts, let's understand a practical example:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │────│ Nginx │────│ Backend 1 │
│ │ │ (Port 8080)│ │ (PHP) │
└─────────────┘ │ │ └─────────────┘
│ │
│ │ ┌─────────────┐
│ │────│ Backend 2 │
└─────────────┘ │ (PHP) │
└─────────────┘
The application consists of:
Frontend: Static HTML contact form served by nginx
Two PHP Back-ends: Process form submissions and show container IDs with a message
nginx: Acts as web server, reverse proxy, and load balancer
Docker: Orchestrates all services
Nginx as a Web Server
What is a Web Server?
A web server is software that serves web content to clients (browsers) over HTTP/HTTPS. When you type a URL in your browser, you're making a request to a web server.
How nginx Works as a Web Server
In this example, nginx serves the static HTML contact form directly to users:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
Key Points:
Nginx listens on port 80 for incoming HTTP requests
Static files (HTML, CSS, JS) are served directly from
/usr/share/nginx/html
Clean, minimal configuration for serving static content
Advantages of Nginx as a Web Server:
Speed: Handles static content incredibly fast
Low Memory Usage: Uses an event-driven architecture
Concurrent Connections: Can handle thousands of simultaneous connections
Compression: Built-in gzip compression reduces bandwidth usage
Nginx as a Reverse Proxy
What is a Reverse Proxy?
A reverse proxy sits between clients and servers, forwarding client requests to appropriate backend servers and then returning the server's response back to the client. Unlike a forward proxy (which acts on behalf of clients), a reverse proxy acts on behalf of servers.
How reverse proxy use in this example
When submit the contact form, nginx forwards the request to our PHP back-ends:
# Proxy API requests to backend servers
location /api/ {
# Remove /api prefix when forwarding to backend
rewrite ^/api/(.*)$ /$1 break;
# Proxy to upstream backend servers
proxy_pass http://backend;
# Headers for proper proxying
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;
}
How it works:
Browser submits form to
/api/contact.php
nginx receives the request
nginx removes
/api
prefix and forwards to backendBackend processes the request
nginx returns the response to the browser
Benefits of Reverse Proxy:
Security: Hides backend server details from clients
SSL Termination: Handles encryption/decryption
Caching: Can cache responses to improve performance
Compression: Compresses responses before sending to clients
Request Routing: Routes requests based on various criteria
Nginx as a Load Balancer
What is Load Balancing?
Load balancing distributes incoming network traffic across multiple servers to ensure no single server becomes overwhelmed. This improves application responsiveness and availability.
Load balancing in this example
Our Nginx configuration uses upstream servers for load balancing:
upstream backend_servers {
server backend1:80;
server backend2:80;
# Explicitly use round-robin (default, but let's be clear)
}
Critical Configuration for Effective Load Balancing:
location /api/ {
proxy_pass http://backend_servers/;
# These settings ensure proper load balancing
proxy_http_version 1.1;
proxy_set_header Connection "";
}8
Why these settings matter:
proxy_http_version 1.1
: Forces HTTP/1.1 protocolproxy_set_header Connection ""
: Prevents connection reuseResult: Each request gets a fresh connection, ensuring visible round-robin distribution
Load Balancing Methods:
Round Robin (this example): Requests are distributed sequentially
Least Connections: Routes to server with fewest active connections
IP Hash: Routes based on client IP hash
Weighted: Assigns different weights to servers
More info: https://www.geeksforgeeks.org/system-design/load-balancing-algorithms/
Demonstrating Load Balancing
When you submit forms in our application, you'll notice:
First submission → Backend 1 (shows container ID)
Second submission → Backend 2 (shows different container ID)
Third submission → Backend 1 (cycle repeats)
This demonstrates round-robin load balancing in action!
Apache vs nginx: Backend Processing
Apache Web Server
Apache uses a process-based or thread-based approach:
Apache Architecture:
┌─────────────┐
│ Request │
└─────┬───────┘
│
┌─────▼───────┐
│ Process/ │ ← Each request gets its own process/thread
│ Thread │
└─────┬───────┘
│
┌─────▼───────┐
│ PHP │ ← PHP processes the request
│ Handler │
└─────────────┘
Apache Characteristics:
Process/Thread per connection: More memory usage
Synchronous processing: Blocks while waiting for I/O
Easier configuration: More straightforward for beginners
Module system: Extensive module ecosystem
How Our PHP Back-ends Work with Apache
In the Docker setup, each backend container runs Apache with PHP:
# Our backends use php:8.1-apache image which includes:
# - Apache web server
# - PHP interpreter
# - mod_php module for Apache
The PHP code processes form data:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = htmlspecialchars($_POST['name'] ?? '');
$email = htmlspecialchars($_POST['email'] ?? '');
$server = gethostname(); // Shows which container processed it
// Clean, inline HTML response with styling
echo "<div style='font-family: Arial, sans-serif; max-width: 400px; margin: 50px auto; padding: 30px; background: #f8f9fa; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);'>";
echo "<h2 style='color: #28a745; text-align: center;'>✓ Message Sent Successfully!</h2>";
echo "<p><strong>Name:</strong> $name</p>";
echo "<p><strong>Email:</strong> $email</p>";
echo "<p style='font-size: 12px; color: #666; text-align: center; margin-top: 20px;'>Processed by Backend Server: $server</p>";
echo "</div>";
}
?>
Nginx vs Apache Performance
Aspect | Nginx | Apache |
Architecture | Event-driven, asynchronous | Process/thread-based |
Memory Usage | Low, constant | Higher, scales with connections |
Static Content | Excellent | Good |
Dynamic Content | Requires backend (PHP-FPM) | Built-in (mod_php) |
Configuration | Simpler syntax | More complex but flexible |
Modules | Compiled-in | Dynamic loading |
Running the Demo Application
Prerequisites
# Install Docker and Docker Compose
sudo apt update
sudo apt install docker.io docker-compose
# Add user to docker group
sudo usermod -aG docker $USER
Setup and Run
# Clone or create the project structure
mkdir simple-nginx-app && cd simple-nginx-app
# Create all files as shown in the first artifact
# Then run:
docker-compose up -d
# Access at: http://localhost:8080
Testing Load Balancing
Fill out the contact form
Submit multiple times
Notice how container IDs alternate between submissions
This demonstrates round-robin load balancing!
Visual Demo: Round Robin Load Balancing in Action
Once the application is up and running, can observe the backend switching behavior using container IDs.
✅ Step 1: Check Running Containers
Run the following command to view active containers and their IDs:
docker ps
List of running containers with their names and container IDs
✅ Step 2: First Form Submission
Open the app in your browser at http://localhost:8080
Fill in the contact form with test values (e.g.,
Alice
,alice@example.com
)Click Submit
Filled contact form before submission
Output showing “Message Sent Successfully!” along with a container ID (e.g., backend1)
✅ Step 3: Second Form Submission
Repeat the process with different values (e.g., Bob
, bob@example.com
):
Second form submission
Response showing success message with a different container ID (e.g., backend2)
✅ Step 4: Repeat to Observe Load Balancing
Each time you submit the form, you'll notice the container ID switches between backend1 and backend2. This confirms the round robin load balancing behavior configured in nginx.
Demonstration of backend switching between requests
Troubleshooting Common Issues
1. 502 Bad Gateway
Backend servers are down
Network connectivity issues
Check
docker-compose logs
2. Load Balancing Not Working
Verify upstream configuration
Check backend server status
Review Nginx error logs
Conclusion
nginx's versatility as a web server, reverse proxy, and load balancer makes it an essential tool for modern web applications. This Docker example demonstrates these concepts in action:
Web Server: Efficiently serves static HTML content
Reverse Proxy: Routes API requests to backend services
Load Balancer: Distributes traffic across multiple PHP back-ends using round-robin
The combination of Nginx with containerized back-ends provides a robust, scalable architecture that can handle high traffic while maintaining excellent performance. Whether you're building microservices, implementing high availability, or optimizing web application performance, understanding these concepts is crucial for modern web development.
Subscribe to my newsletter
Read articles from Sachindu Malshan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
