intermediate devops Nginx 1.25+ · Updated April 2026

Nginx Configuration Cheatsheet

Master Nginx configuration for web serving, reverse proxying, and load balancing with practical, runnable examples.

· 8 min read · AI-reviewed

## 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

Getting Started

To get Nginx running and serving content, you typically install it, create a basic configuration file, and then start the service.

  1. 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
  2. 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.

  3. 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!”.

Core Concepts

Understanding these fundamental Nginx concepts is crucial for effective configuration.

ConceptDescription
DirectiveA configuration instruction, always ending with a semicolon. Can be simple (listen 80;) or block-level (server { ... }).
ContextThe scope where directives are allowed. Main contexts: main, events, http, “server, location, upstream`. Directives inherit from parent contexts.
server BlockDefines a virtual host. Nginx decides which server block to use based on listen (IP/port) and server_name (domain name).
location BlockDefines 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 BlockUsed for load balancing, defining a group of backend servers to which Nginx will proxy requests.
ModuleNginx’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_filesA 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_passDirects requests to another server (e.g., an application server or an upstream group). Key for reverse proxying.

Essential Commands / API / Syntax

This section covers the most common Nginx configuration patterns.

Nginx Service Management

# 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

Basic Server Block (HTTP)

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;
}

HTTPS Configuration (SSL/TLS)

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;
}

Reverse Proxying

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;
}

Load Balancing

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;
}

Rewrites and Redirects

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
    }
}

Access Control & Security

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;
    }
}

Common Patterns

1. Frontend SPA + Backend API Proxy

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;
}

2. Rate Limiting

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;
        }
    }
}

Gotchas & Tips

  • 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).
  • Test Your Config: Always run sudo nginx -t before reloading or restarting Nginx. This validates your configuration syntax and prevents downtime from typos.
  • Security Headers (Nginx 1.25+): Add security headers to your 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
  • Caching: Nginx can cache responses for static files or proxied content, significantly reducing backend load and improving performance. Look into proxy_cache_path and proxy_cache directives for advanced caching.
  • Logging: Configure specific access_log and error_log directives for each server block to simplify debugging and monitoring. Custom log formats (log_format) provide more granular information.

Next Steps

  • Official Nginx Documentation: The ultimate source for comprehensive information on every directive and module. nginx.org/en/docs/
  • Let’s Encrypt with Certbot: Easily obtain and manage free SSL certificates for your Nginx servers. certbot.eff.org
  • z2h.fyi/cheatsheets/linux-cli: Brush up on your Linux command line skills for easier server management.

Source: z2h.fyi/cheatsheets/nginx-configuration — Zero to Hero cheatsheets for developers.

---

*Source: [z2h.fyi/cheatsheets/nginx-configuration-cheatsheet](https://z2h.fyi/cheatsheets/nginx-configuration-cheatsheet) — Zero to Hero cheatsheets for developers.*