Nginx Unleashed: From Static Site Serving to Dynamic Load Balancing
Nginx (pronounced “engine‑x”) has become the de‑facto web server for everything from personal blogs to massive SaaS platforms. Its event‑driven architecture lets it handle tens of thousands of concurrent connections while using a fraction of the memory that traditional thread‑per‑connection servers need. In this post we’ll walk through a practical setup that starts with serving a static site, then evolves into a full‑blown reverse proxy with SSL termination and round‑robin load balancing. By the end you’ll have a production‑ready nginx.conf you can drop into any Linux box.
1. Quick Installation
Ubuntu / Debian
sudo apt update
sudo apt install -y nginx
CentOS / RHEL
sudo yum install -y epel-release
sudo yum install -y nginx
After installation, start and enable the service:
sudo systemctl start nginx
sudo systemctl enable nginx
You can verify it’s running with curl -I http://localhost. You should see a 200 OK and the default Welcome to nginx! page.
2. Serving a Static Site
Create a simple site in /var/www/myapp:
sudo mkdir -p /var/www/myapp
sudo chown $USER:$USER /var/www/myapp
echo "<h1>Hello from Nginx</h1>" > /var/www/myapp/index.html
Now replace the default server block. Edit /etc/nginx/sites-available/default (or /etc/nginx/conf.d/default.conf on some distros):
server {
listen 80;
server_name example.com www.example.com;
root /var/www/myapp;
index index.html;
# Optional: clean URLs
location / {
try_files $uri $uri/ =404;
}
# Log format for later analysis
access_log /var/log/nginx/access.log combined;
error_log /var/log/nginx/error.log warn;
}
Test the configuration and reload:
sudo nginx -t && sudo systemctl reload nginx
Visiting http://example.com now serves your static index.html. Simple, fast, and secure (the server never spawns a new process per request).
3. Adding a Reverse Proxy
Most modern applications run behind an application server (Node, Django, Go, etc.). Let’s proxy /api to a backend running on localhost:3000.
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
# WebSocket support
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
The proxy_set_header directives preserve the original request metadata, which is critical for logging and authentication in the upstream app.
4. SSL Termination with Let’s Encrypt
Secure traffic should be the default. We’ll use Certbot to obtain a free certificate and let Nginx handle TLS.
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Certbot automatically modifies the server block:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Rest of the config (root, location, etc.) stays the same
}
It also creates a redirect from HTTP to HTTPS:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Remember to schedule certificate renewal:
sudo systemctl enable certbot.timer
5. Load Balancing Across Multiple Backends
When traffic spikes, a single backend can become a bottleneck. Nginx’s upstream module makes load balancing painless.
# Define the pool of app servers
upstream app_cluster {
least_conn; # Choose the server with the fewest active connections
server 10.0.0.101:3000;
server 10.0.0.102:3000;
server 10.0.0.103:3000 backup; # Optional: a standby node
}
Now point the proxy to the upstream group:
server {
listen 443 ssl http2;
server_name api.example.com;
location / {
proxy_pass http://app_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# ... other headers as before
}
}
You can experiment with different algorithms (round_robin, ip_hash, hash $request_uri) based on your workload characteristics.
6. Performance Tweaks
a. Gzip Compression
gzip on;
gzip_types text/css application/javascript text/xml application/json;
gzip_min_length 256;
gzip_proxied any;
b. Caching Static Assets
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
c. Connection Limits
# Prevent a single IP from hogging connections
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
limit_conn addr 10; # max 10 concurrent connections per IP
# ... rest of the block
}
d. Worker Processes & Connections
Edit /etc/nginx/nginx.conf:
worker_processes auto; # Auto‑detect CPU cores
events {
worker_connections 4096; # Max connections per worker
multi_accept on;
use epoll; # Linux‑specific, best performance
}
These settings give Nginx a solid baseline for high‑throughput environments.
7. Debugging & Monitoring
- Access & error logs: Tail them in real time.
sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log
- Status module: Enable a lightweight endpoint for metrics.
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
Then query curl http://127.0.0.1:8080/nginx_status to see active connections, request rates, etc.
- Third‑party tools: Grafana + Prometheus, or the NGINX Amplify service for deeper insights.
8. Putting It All Together
Below is a consolidated nginx.conf that incorporates static serving, SSL, reverse proxy, load balancing, and performance tweaks.
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
# Gzip
gzip on;
gzip_types text/css application/javascript text/xml application/json;
gzip_min_length 256;
gzip_proxied any;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Upstream pool
upstream app_cluster {
least_conn;
server 10.0.0.101:3000;
server 10.0.0.102:3000;
server 10.0.0.103:3000 backup;
}
# Status endpoint
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
# Main site – static + SSL
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/myapp;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# API reverse proxy + load balancing
server {
listen 80;
server_name api.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://app_cluster;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
Run sudo nginx -t && sudo systemctl reload nginx after saving. Your stack is now capable of handling static content, encrypted traffic, and scaling across multiple backend instances—all with minimal resource consumption.
9. Conclusion
Nginx’s modular design means you can start small—just a static file server—and grow into a full‑featured edge proxy without rewriting configuration. By mastering the core directives (listen, server_name, location, proxy_pass, upstream) and a handful of performance knobs, you gain a resilient, low‑latency front line for any web application.
Happy tuning, and may your connections stay ever‑alive!
Recommended Tools
DigitalOcean Droplets – Spin up cheap, SSD‑backed VMs in seconds and install Nginx with a single click. Get a $100 credit for new users: https://www.digitalocean.com/?refcode=YOURAFFILIATEID
NGINX Amplify – A free monitoring agent that visualizes metrics, alerts on anomalies, and offers configuration recommendations. Sign up here: https://amplify.nginx.com/signup/?ref=YOURAFFILIATEID
These tools complement the Nginx setup and help you keep your infrastructure performant and observable.
Top comments (0)