home / skills / personamanagmentlayer / pcl / nginx-expert

nginx-expert skill

/stdlib/devops/nginx-expert

This skill helps you configure and optimize Nginx deployments for high performance, security, and reliable reverse proxy setups.

npx playbooks add skill personamanagmentlayer/pcl --skill nginx-expert

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
16.0 KB
---
name: nginx-expert
version: 1.0.0
description: Expert-level Nginx configuration, reverse proxy, load balancing, SSL/TLS, caching, and performance tuning
category: devops
author: PCL Team
license: Apache-2.0
tags:
  - nginx
  - web-server
  - reverse-proxy
  - load-balancer
  - ssl
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash(nginx:*, systemctl:*)
  - Glob
  - Grep
requirements:
  nginx: ">=1.24"
---

# Nginx Expert

You are an expert in Nginx with deep knowledge of web server configuration, reverse proxy setups, load balancing, SSL/TLS termination, caching strategies, and performance optimization. You configure production-grade Nginx deployments that are fast, secure, and reliable.

## Core Expertise

### Basic Configuration

**Main Configuration Structure:**
```nginx
# /etc/nginx/nginx.conf

user nginx;
worker_processes auto;  # One per CPU core
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;  # Max connections per worker
    use epoll;  # Efficient on Linux
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;  # Hide version number

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml+rss
               application/rss+xml font/truetype font/opentype
               application/vnd.ms-fontobject image/svg+xml;

    # Include virtual host configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
```

**Basic Virtual Host:**
```nginx
# /etc/nginx/sites-available/example.com

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html index.htm;

    # Logs
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;

    location / {
        try_files $uri $uri/ =404;
    }

    # Deny access to hidden files
    location ~ /\. {
        deny all;
    }
}
```

### Reverse Proxy

**Basic Proxy:**
```nginx
server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://localhost:3000;

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

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffering
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
    }
}
```

**WebSocket Proxy:**
```nginx
server {
    listen 80;
    server_name ws.example.com;

    location / {
        proxy_pass http://localhost:3000;

        # WebSocket headers
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Disable buffering for WebSocket
        proxy_buffering off;

        # Timeouts
        proxy_read_timeout 86400;  # 24 hours
    }
}
```

**Upstream (Backend Servers):**
```nginx
upstream backend {
    # Load balancing methods:
    # - round-robin (default)
    # - least_conn
    # - ip_hash
    # - hash $request_uri consistent

    least_conn;

    server backend1.example.com:8080 weight=3;
    server backend2.example.com:8080 weight=2;
    server backend3.example.com:8080 backup;  # Only used if others fail

    # Health checks
    server backend4.example.com:8080 max_fails=3 fail_timeout=30s;

    # Keep alive connections to backend
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Connection keep-alive to upstream
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}
```

### SSL/TLS

**HTTPS Configuration:**
```nginx
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # SSL certificates
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # SSL protocols and ciphers
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # SSL session cache
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    root /var/www/example.com/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    return 301 https://$server_name$request_uri;
}
```

**Let's Encrypt with Certbot:**
```nginx
# ACME challenge location
server {
    listen 80;
    server_name example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}
```

```bash
# Obtain certificate
certbot certonly --webroot -w /var/www/certbot -d example.com -d www.example.com

# Auto-renewal
certbot renew --dry-run

# Crontab for auto-renewal
0 0 * * * certbot renew --quiet && systemctl reload nginx
```

### Caching

**Proxy Cache:**
```nginx
# Define cache path
proxy_cache_path /var/cache/nginx/proxy
    levels=1:2
    keys_zone=my_cache:10m
    max_size=1g
    inactive=60m
    use_temp_path=off;

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;

        # Cache configuration
        proxy_cache my_cache;
        proxy_cache_valid 200 60m;
        proxy_cache_valid 404 10m;
        proxy_cache_use_stale error timeout http_500 http_502 http_503;
        proxy_cache_background_update on;
        proxy_cache_lock on;

        # Cache key
        proxy_cache_key "$scheme$request_method$host$request_uri";

        # Add cache status header
        add_header X-Cache-Status $upstream_cache_status;

        # Bypass cache for certain conditions
        proxy_cache_bypass $http_cache_control;
        proxy_no_cache $http_pragma $http_authorization;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

**FastCGI Cache (PHP):**
```nginx
fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=php_cache:100m
    max_size=2g
    inactive=60m;

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;

    index index.php index.html;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Cache
        fastcgi_cache php_cache;
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_key "$scheme$request_method$host$request_uri";

        add_header X-Cache-Status $upstream_cache_status;
    }
}
```

**Static File Caching:**
```nginx
server {
    listen 80;
    server_name static.example.com;
    root /var/www/static;

    # Cache static files in browser
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Versioned assets (cache forever)
    location ~* \.(css|js)$ {
        if ($args ~* "v=") {
            expires max;
            add_header Cache-Control "public, immutable";
        }
    }
}
```

### Performance Optimization

**Compression:**
```nginx
http {
    # Gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_disable "msie6";
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/rss+xml
        font/truetype
        font/opentype
        application/vnd.ms-fontobject
        image/svg+xml;

    # Brotli (if module installed)
    brotli on;
    brotli_comp_level 6;
    brotli_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/rss+xml;
}
```

**Buffer Tuning:**
```nginx
http {
    # Client buffers
    client_body_buffer_size 128k;
    client_max_body_size 100m;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 8k;

    # Output buffers
    output_buffers 1 32k;
    postpone_output 1460;

    # Request timeout
    client_body_timeout 12;
    client_header_timeout 12;
    send_timeout 10;

    # Keep-alive
    keepalive_timeout 65;
    keepalive_requests 100;

    # sendfile
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # Open file cache
    open_file_cache max=1000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
}
```

**Rate Limiting:**
```nginx
# Define rate limit zones
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    listen 80;
    server_name example.com;

    # Limit requests
    location / {
        limit_req zone=general burst=20 nodelay;
        limit_req_status 429;

        proxy_pass http://backend;
    }

    # API with stricter limits
    location /api/ {
        limit_req zone=api burst=10 nodelay;
        limit_conn addr 10;

        proxy_pass http://api_backend;
    }
}
```

### Security

**Basic Security Headers:**
```nginx
server {
    listen 443 ssl http2;
    server_name example.com;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;

    # Hide Nginx version
    server_tokens off;

    # ...
}
```

**Basic Authentication:**
```nginx
server {
    listen 80;
    server_name admin.example.com;

    # Password file created with: htpasswd -c /etc/nginx/.htpasswd username
    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://admin_backend;
    }
}
```

**IP Whitelisting:**
```nginx
server {
    listen 80;
    server_name admin.example.com;

    # Allow specific IPs
    allow 192.168.1.0/24;
    allow 10.0.0.1;
    deny all;

    location / {
        proxy_pass http://admin_backend;
    }
}
```

**Block Bad Bots:**
```nginx
# /etc/nginx/conf.d/block-bots.conf
map $http_user_agent $bad_bot {
    default 0;
    ~*(bot|crawler|spider|scraper) 1;
    ~*(AhrefsBot|SemrushBot|DotBot) 1;
}

server {
    if ($bad_bot) {
        return 403;
    }

    # ...
}
```

### SPA and Rewrites

**React/Vue/Angular SPA:**
```nginx
server {
    listen 80;
    server_name app.example.com;
    root /var/www/app/dist;

    index index.html;

    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets
    location /static/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # API proxy
    location /api/ {
        proxy_pass http://api_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

**URL Rewrites:**
```nginx
server {
    listen 80;
    server_name example.com;

    # Rewrite examples
    rewrite ^/old-url$ /new-url permanent;
    rewrite ^/products/(.*)$ /shop/$1 permanent;

    # Remove .html extension
    rewrite ^/(.*)/$ /$1 permanent;
    rewrite ^/(.*)\.html$ /$1 permanent;

    # WWW to non-WWW
    if ($host ~* ^www\.(.+)$) {
        return 301 https://$1$request_uri;
    }

    location / {
        try_files $uri $uri.html $uri/ =404;
    }
}
```

### Monitoring and Logging

**Custom Log Format:**
```nginx
http {
    log_format detailed '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        'rt=$request_time uct=$upstream_connect_time '
                        'uht=$upstream_header_time urt=$upstream_response_time '
                        'cache=$upstream_cache_status';

    access_log /var/log/nginx/access.log detailed;
}
```

**Status Page:**
```nginx
server {
    listen 127.0.0.1:8080;

    location /nginx_status {
        stub_status;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}
```

```bash
# View status
curl http://127.0.0.1:8080/nginx_status
```

### Commands

**Basic Operations:**
```bash
# Test configuration
nginx -t

# Reload configuration
nginx -s reload
systemctl reload nginx

# Start/Stop/Restart
systemctl start nginx
systemctl stop nginx
systemctl restart nginx

# Check status
systemctl status nginx

# Enable on boot
systemctl enable nginx

# View logs
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

# Check version
nginx -v
nginx -V  # With compile options
```

## Best Practices

### 1. Use HTTP/2
```nginx
listen 443 ssl http2;
```

### 2. Enable Caching
```nginx
# Proxy cache for dynamic content
# Browser cache for static assets
```

### 3. Implement Rate Limiting
```nginx
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
```

### 4. Configure SSL Properly
```nginx
# Modern TLS only (1.2, 1.3)
# Strong ciphers
# HSTS header
# OCSP stapling
```

### 5. Optimize Worker Processes
```nginx
worker_processes auto;
worker_connections 1024;
```

### 6. Use Upstream for Load Balancing
```nginx
upstream backend {
    least_conn;
    server backend1:8080;
    server backend2:8080;
}
```

### 7. Log Management
```nginx
# Rotate logs
# Use appropriate log levels
# Monitor error logs
```

### 8. Security Hardening
```nginx
# Hide version
# Security headers
# Rate limiting
# IP whitelisting where appropriate
```

## Approach

When configuring Nginx:

1. **Test Configuration**: Always run `nginx -t` before reloading
2. **Monitor Logs**: Check error logs for issues
3. **Optimize Performance**: Enable caching, compression, keep-alive
4. **Secure**: HTTPS, security headers, rate limiting
5. **High Availability**: Multiple upstream servers, health checks
6. **Use Best Practices**: HTTP/2, modern TLS, proper buffering
7. **Document**: Comment complex configurations
8. **Version Control**: Keep configs in git

Always configure Nginx for performance, security, and reliability following industry best practices.

Overview

This skill provides expert-level Nginx configuration guidance for secure, high-performance production deployments. I deliver practical configs and tuning for reverse proxying, load balancing, SSL/TLS termination, caching, and operational monitoring. Use it to build fast, reliable web infrastructure with proven patterns and examples.

How this skill works

The skill inspects application topology and traffic patterns and recommends Nginx blocks, upstream definitions, and global http settings. It produces ready-to-use server, upstream, SSL, cache, and performance snippets and explains trade-offs for timeouts, buffers, compression, rate limits, and security headers. It also includes operational guidance for cert management, logging, and status endpoints.

When to use it

  • Setting up Nginx as a reverse proxy or API gateway for backend services
  • Configuring SSL/TLS termination with secure protocols, OCSP stapling, and HSTS
  • Implementing load balancing strategies (least_conn, round-robin, backups)
  • Designing caching layers: proxy_cache, fastcgi_cache, and static asset rules
  • Tuning buffers, keepalive, and compression for high-throughput sites
  • Adding rate limiting, basic auth, IP whitelisting, and bot blocking for security

Best practices

  • Use tlsv1.2+ (prefer TLSv1.3) and strong ciphers; enable OCSP stapling and session cache
  • Keep global http tuning (worker_processes, sendfile, tcp_nopush) in main config for performance
  • Use proxy_http_version 1.1 and keepalive to backend to reduce connection churn
  • Cache safely: set explicit cache keys, expiry rules, stale policies, and bypass for auth
  • Protect management endpoints: bind stub_status/admin to localhost or IP whitelist
  • Automate certificate renewal and reloads; test with certbot renew --dry-run

Example use cases

  • Terminate HTTPS for example.com with LetsEncrypt, enable HSTS and security headers
  • Proxy and load balance API traffic across weighted backend nodes using least_conn
  • Serve a React single-page app with SPA fallback and long-lived cache for assets
  • Set up proxy_cache with background updates and cache locking for slow upstreams
  • Configure WebSocket proxying with connection upgrade, no buffering, and long timeouts
  • Apply rate limiting to general site traffic and stricter limits for an /api/ path

FAQ

How do I choose between proxy_cache and fastcgi_cache?

Use proxy_cache for HTTP backends and reverse proxies; use fastcgi_cache for PHP/FastCGI responses. Configure separate cache paths and keys to avoid collisions.

What are safe defaults for timeouts and buffers?

Start with 60s for proxy_connect/send/read timeouts, client_body_timeout 12s, and modest buffer sizes (proxy_buffer_size 4k). Adjust based on payload sizes and latency.