Nginx Configuration Cheatsheet
Master Nginx configuration for web serving, reverse proxying, and load balancing with practical, runnable examples.
Master Nginx configuration for web serving, reverse proxying, and load balancing with practical, runnable examples.
## Quick Overview
Nginx (pronounced "engine-x") is a powerful, high-performance HTTP and reverse proxy server, as well as a mail proxy server and a generic TCP/UDP proxy server. It's renowned for its stability, rich feature set, simple configuration, and low resource consumption. Developers commonly reach for Nginx to serve static content, load balance traffic across multiple backend servers, cache responses, and act as a secure gateway for APIs or applications. This guide covers Nginx configurations from basic web serving to advanced proxying, focusing on version `1.25+`.
**Install (Ubuntu/Debian):**
```bash
# Update package list
sudo apt update
# Install Nginx
sudo apt install nginx
To get Nginx running and serving content, you typically install it, create a basic configuration file, and then start the service.
Install Nginx (if not already):
# For Debian/Ubuntu
sudo apt update
sudo apt install nginx
# For RHEL/CentOS
sudo yum install epel-release
sudo yum install nginx
Verify Nginx is Running:
# Check service status
sudo systemctl status nginx
# Expected output should show 'active (running)'
If not running, start it:
sudo systemctl start nginx
Access http://localhost in your browser. You should see the default Nginx welcome page.
Basic Static File Server: Let’s create a simple HTML file and configure Nginx to serve it.
Create a simple index.html:
# Create a directory for our site
sudo mkdir -p /var/www/my-app.com/html
# Create an index.html file
echo "<h1>Hello from my-app.com!</h1>" | sudo tee /var/www/my-app.com/html/index.html
Create a new Nginx server block configuration file:
sudo nano /etc/nginx/sites-available/my-app.com
Add the following content:
# /etc/nginx/sites-available/my-app.com
server {
listen 80; # Listen for incoming HTTP requests on port 80
listen [::]:80; # Listen for IPv6 requests
server_name my-app.com www.my-app.com; # Define the domain names this server block responds to
root /var/www/my-app.com/html; # Set the document root for this server block
index index.html index.htm; # Define default files to serve when a directory is requested
location / {
try_files $uri $uri/ =404; # Try to serve the requested file, then directory, otherwise return 404
}
# Log files for this server block
access_log /var/log/nginx/my-app.com-access.log;
error_log /var/log/nginx/my-app.com-error.log;
}
Enable the site by symlinking it to sites-enabled:
sudo ln -s /etc/nginx/sites-available/my-app.com /etc/nginx/sites-enabled/
Test Nginx configuration for syntax errors:
sudo nginx -t
# Expected output: "test is successful"
Reload Nginx to apply changes:
sudo systemctl reload nginx
(Optional) Add my-app.com to your /etc/hosts file for local testing:
echo "127.0.0.1 my-app.com www.my-app.com" | sudo tee -a /etc/hosts
Now, navigate to http://my-app.com in your browser. You should see “Hello from my-app.com!”.
Understanding these fundamental Nginx concepts is crucial for effective configuration.
| Concept | Description |
|---|---|
| Directive | A configuration instruction, always ending with a semicolon. Can be simple (listen 80;) or block-level (server { ... }). |
| Context | The scope where directives are allowed. Main contexts: main, events, http, “server, location, upstream`. Directives inherit from parent contexts. |
server Block | Defines a virtual host. Nginx decides which server block to use based on listen (IP/port) and server_name (domain name). |
location Block | Defines how Nginx handles requests for different URIs within a server block. Can match prefixes, exact URIs, or regular expressions. Order matters for regex locations. |
upstream Block | Used for load balancing, defining a group of backend servers to which Nginx will proxy requests. |
| Module | Nginx’s functionality is organized into modules (e.g., http_proxy_module, http_ssl_module). Directives belong to specific modules. Most common ones are compiled in by default. |
try_files | A directive used within location blocks to check for the existence of files or directories in a specified order and then serve them or pass the request to another handler. Prevents unnecessary internal redirects. |
proxy_pass | Directs requests to another server (e.g., an application server or an upstream group). Key for reverse proxying. |
This section covers the most common Nginx configuration patterns.
# Test configuration syntax without reloading (essential!)
sudo nginx -t
# Reload Nginx to apply new configurations (without dropping connections)
sudo systemctl reload nginx
# Restart Nginx (drops existing connections, use if reload fails or for major changes)
sudo systemctl restart nginx
# Stop Nginx
sudo systemctl stop nginx
# Start Nginx
sudo systemctl start nginx
# Check Nginx status
sudo systemctl status nginx
Configuring Nginx to respond to a domain name and serve static files.
# /etc/nginx/sites-available/my-website.com
server {
listen 80; # Listen for HTTP requests
server_name my-website.com www.my-website.com; # Domain names
root /var/www/my-website.com/html; # Document root
index index.html index.htm; # Default files
location / {
try_files $uri $uri/ =404; # Serve file, then directory, or 404
}
access_log /var/log/nginx/my-website.com-access.log;
error_log /var/log/nginx/my-website.com-error.log;
}
Securing your site with SSL/TLS. Requires obtaining an SSL certificate (e.g., from Let’s Encrypt).
# /etc/nginx/sites-available/my-secure-app.com
server {
listen 80; # Redirect all HTTP traffic to HTTPS
server_name my-secure-app.com www.my-secure-app.com;
return 301 https://$host$request_uri; # Permanent redirect
}
server {
listen 443 ssl http2; # Listen for HTTPS requests
listen [::]:443 ssl http2;
server_name my-secure-app.com www.my-secure-app.com;
# SSL certificate paths (replace with your actual paths)
ssl_certificate /etc/letsencrypt/live/my-secure-app.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my-secure-app.com/privkey.pem;
# Basic SSL optimizations (Nginx 1.25+)
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM"; # Strong ciphers
ssl_prefer_server_ciphers on;
ssl_stapling on; # OCSP stapling
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s; # Google Public DNS
resolver_timeout 5s;
root /var/www/my-secure-app.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
access_log /var/log/nginx/my-secure-app.com-access.log;
error_log /var/log/nginx/my-secure-app.com-error.log;
}
Directing traffic to an application server (e.g., Node.js, Python, Java).
# /etc/nginx/sites-available/my-api-proxy.com
server {
listen 80;
server_name my-api-proxy.com;
location / {
# Proxy requests to a backend application running on port 3000
proxy_pass http://127.0.0.1:3000;
# Standard proxy headers
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 proxying (if your backend uses WebSockets)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
access_log /var/log/nginx/my-api-proxy.com-access.log;
error_log /var/log/nginx/my-api-proxy.com-error.log;
}
Distributing requests across multiple backend servers using an upstream block.
# Define the group of backend servers
upstream backend_servers {
server 192.168.1.100:8080 weight=5; # Server with higher weight gets more requests
server 192.168.1.101:8080;
server 192.168.1.102:8080;
# Can also use DNS names for servers
# server backend1.example.com;
# Load balancing methods (default is round-robin)
# least_conn; # Send to server with fewest active connections
# ip_hash; # Ensures requests from same client IP go to same server
# fair; # Requires third-party module
# hash $request_uri consistent; # Distribute based on URI hash
}
server {
listen 80;
server_name my-load-balanced-app.com;
location / {
proxy_pass http://backend_servers; # Proxy to the upstream group
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;
}
access_log /var/log/nginx/lb-app-access.log;
error_log /var/log/nginx/lb-app-error.log;
}
Changing the URI of a request or sending a client to a different URL.
server {
listen 80;
server_name old-domain.com;
# Permanent redirect for an entire domain
return 301 https://new-domain.com$request_uri;
}
server {
listen 80;
server_name example.com;
location /old-path {
# Internal rewrite (Nginx processes the new URI internally)
rewrite ^/old-path(.*)$ /new-path$1 last;
}
location /legacy-page {
# Permanent redirect for a specific page
return 301 /new-page;
}
location ~ ^/articles/(\d+)/?$ { # Regex location match
# Redirect with capture groups
return 301 /blog/post/$1;
}
# Conditional redirect (use `if` sparingly, `try_files` is usually better)
if ($request_method = POST) {
return 405; # Method Not Allowed
}
}
Restricting access based on IP or HTTP Basic Authentication.
server {
listen 80;
server_name restricted.example.com;
# Deny access from specific IPs
deny 192.168.1.1;
allow 192.168.1.0/24; # Allow a subnet
allow all; # Default allow if not denied by rule above
location /admin {
# HTTP Basic Authentication
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd; # Path to htpasswd file
# To create .htpasswd:
# sudo apt install apache2-utils # (if not installed)
# sudo htpasswd -c /etc/nginx/.htpasswd admin_user
# (then enter password for admin_user)
proxy_pass http://127.0.0.1:8001;
}
}
Serving a Single Page Application (SPA) (e.g., React, Angular, Vue) while proxying API requests to a backend.
# /etc/nginx/sites-available/my-spa-app.com
server {
listen 80;
listen [::]:80;
server_name my-spa-app.com www.my-spa-app.com;
# Assuming your SPA build output is in /var/www/my-spa-app.com/build
root /var/www/my-spa-app.com/build;
index index.html;
# Proxy API requests to your backend on port 5000
location /api/ {
proxy_pass http://localhost:5000;
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;
}
# Serve static assets (e.g., JS, CSS, images) directly
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d; # Cache static assets for 30 days
add_header Cache-Control "public, no-transform";
try_files $uri =404;
}
# For any other request, serve the index.html file (SPA routing)
location / {
try_files $uri $uri/ /index.html;
}
access_log /var/log/nginx/my-spa-app.com-access.log;
error_log /var/log/nginx/my-spa-app.com-error.log;
}
Protecting your application from abuse by limiting the number of requests per client IP.
# In the http block (usually /etc/nginx/nginx.conf or a separate file in /etc/nginx/conf.d/)
http {
# Define a shared memory zone for storing request states (Nginx 1.25+)
# 'mylimit' is the zone name, 10m is 10 megabytes, which can store ~160k states
# rate=10r/s means 10 requests per second.
# burst=20 allows bursts of up to 20 requests over the rate.
# nodelay means Nginx will respond immediately for bursts, otherwise it delays.
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s burst=20 nodelay;
server {
listen 80;
server_name my-rate-limited-app.com;
location / {
# Apply the rate limit to this location
# If the rate is exceeded, Nginx returns a 503 (Service Unavailable)
limit_req zone=mylimit;
proxy_pass http://localhost:3000;
}
# Another location with a stricter limit for sensitive APIs
location /api/login {
limit_req zone=mylimit burst=5 nodelay; # Stricter burst
proxy_pass http://localhost:3000;
}
}
}
location Block Order: The order of location blocks matters. Nginx first checks prefix matches, then regex matches. More specific prefix matches take precedence over less specific ones. Regex matches (~ or ~*) are processed in order of appearance in the configuration file, and the first matching regex location is used. Use ^~ for a prefix match that, if matched, stops further regex evaluation.try_files vs. if: Prefer try_files over if directives whenever possible. The if directive can be problematic and lead to unexpected behavior due to its complex processing order within Nginx. try_files is efficient and designed for checking file existence.reload vs. restart: Always use sudo systemctl reload nginx to apply configuration changes. This hot-reloads the configuration without dropping active connections. Only sudo systemctl restart nginx if reload fails or for very significant changes that require a full service restart (rare).sudo nginx -t before reloading or restarting Nginx. This validates your configuration syntax and prevents downtime from typos.http or server blocks for better security.
# Example security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "no-referrer-when-downgrade";
add_header Content-Security-Policy "default-src 'self' data: 'unsafe-inline' 'unsafe-eval' https://*;"; # Tailor this carefully
proxy_cache_path and proxy_cache directives for advanced caching.access_log and error_log directives for each server block to simplify debugging and monitoring. Custom log formats (log_format) provide more granular information.Source: z2h.fyi/cheatsheets/nginx-configuration — Zero to Hero cheatsheets for developers.
---
*Source: [z2h.fyi/cheatsheets/nginx-configuration-cheatsheet](https://zero2hero.run/cheatsheets/nginx-configuration-cheatsheet) — Zero to Hero cheatsheets for developers.*