
Configuring Nginx
- 59 installs
- 426 repo stars
- Updated December 11, 2025
- ancoleman/ai-design-components
Configuring-nginx is a Claude Code skill that configures nginx for static sites, reverse proxying, load balancing, SSL/TLS termination, caching, and performance tuning.
About
Configuring-nginx is a Claude Code skill for configuring nginx as a web server, reverse proxy, and load balancer. A developer uses it when setting up static sites, proxying backend applications, terminating SSL/TLS, or adding caching and rate limiting. It provides production-ready configuration blocks with modern security practices like TLS 1.3, security headers, and rate limiting.
- Static sites, reverse proxy, and load balancing configs
- SSL/TLS termination with TLS 1.3 and HSTS headers
- Caching, rate limiting, and WebSocket proxying
Configuring Nginx by the numbers
- 59 all-time installs (skills.sh)
- Ranked #688 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
configuring-nginx capabilities & compatibility
- Capabilities
- configuring nginx · configuring firewalls · deploying applications
- Works with
- docker
- Use cases
- devops
- Platforms
- Linux
What configuring-nginx says it does
Configure nginx for static sites, reverse proxying, load balancing, SSL/TLS termination, caching, and performance tuning.
ssl_protocols TLSv1.3 TLSv1.2;
npx skills add https://github.com/ancoleman/ai-design-components --skill configuring-nginxAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 59 |
|---|---|
| repo stars | ★ 426 |
| Last updated | December 11, 2025 |
| Repository | ancoleman/ai-design-components ↗ |
What it does
Configure nginx as a reverse proxy or load balancer with SSL/TLS termination and caching.
Who is it for?
Setting up nginx as a web server, reverse proxy, or load balancer with HTTPS.
Skip if: Application code or non-nginx web servers.
When should I use this skill?
Configuring nginx for static sites, reverse proxying, or SSL termination.
What you get
Production-ready nginx configs with modern TLS, security headers, and performance tuning.
- Static site nginx config
- Reverse proxy config
- SSL/TLS server block
By the numbers
- nginx location matching evaluated in priority order
Files
Configuring nginx
Purpose
Guide engineers through configuring nginx for common web infrastructure needs: static file serving, reverse proxying backend applications, load balancing across multiple servers, SSL/TLS termination, caching, and performance optimization. Provides production-ready configurations with security best practices.
When to Use This Skill
Use when working with:
- Setting up web server for static sites or single-page applications
- Configuring reverse proxy for Node.js, Python, Ruby, or Go applications
- Implementing load balancing across multiple backend servers
- Terminating SSL/TLS for HTTPS traffic
- Adding caching layer for performance improvement
- Building API gateway functionality
- Protecting against DDoS with rate limiting
- Proxying WebSocket connections
Trigger phrases: "configure nginx", "nginx reverse proxy", "nginx load balancer", "enable SSL in nginx", "nginx performance tuning", "nginx caching", "nginx rate limiting"
Installation
Ubuntu/Debian:
sudo apt update && sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginxRHEL/CentOS/Rocky:
sudo dnf install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginxDocker:
docker run -d -p 80:80 -v /path/to/config:/etc/nginx/conf.d nginx:alpineQuick Start Examples
Static Website
Serve HTML/CSS/JS files from a directory:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Enable site:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxSee references/static-sites.md for SPA configurations and advanced patterns.
Reverse Proxy
Proxy requests to a backend application server:
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_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;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}See references/reverse-proxy.md for WebSocket proxying and API gateway patterns.
SSL/TLS Configuration
Enable HTTPS with modern TLS configuration:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}See references/ssl-tls-config.md for complete TLS configuration and certificate setup.
Core Concepts
Configuration Structure
nginx uses hierarchical configuration contexts:
nginx.conf (global settings)
├── events { } (connection processing)
└── http { } (HTTP-level settings)
└── server { } (virtual host)
└── location { } (URL routing)File locations:
/etc/nginx/nginx.conf- Main configuration/etc/nginx/sites-available/- Available site configs/etc/nginx/sites-enabled/- Enabled sites (symlinks)/etc/nginx/conf.d/*.conf- Additional configs/etc/nginx/snippets/- Reusable config snippets
See references/configuration-structure.md for detailed anatomy.
Location Matching Priority
nginx evaluates location blocks in this order:
1. location = /exact - Exact match (highest priority) 2. location ^~ /prefix - Prefix match, stop searching 3. location ~ \.php$ - Regex, case-sensitive 4. location ~* \.(jpg|png)$ - Regex, case-insensitive 5. location / - Prefix match (lowest priority)
Example:
location = /api/status {
return 200 "OK\n";
}
location ^~ /static/ {
root /var/www;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
location / {
proxy_pass http://backend;
}Essential Proxy Headers
When proxying to backends, preserve client information:
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;Create reusable snippet at /etc/nginx/snippets/proxy-params.conf and include with:
include snippets/proxy-params.conf;Common Patterns
Load Balancing
Distribute traffic across multiple backend servers:
Round Robin (default):
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
}
}Least Connections:
upstream backend {
least_conn;
server backend1.example.com:8080;
server backend2.example.com:8080;
}IP Hash (sticky sessions):
upstream backend {
ip_hash;
server backend1.example.com:8080;
server backend2.example.com:8080;
}Health Checks:
upstream backend {
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backup.example.com:8080 backup;
}See references/load-balancing.md for weighted load balancing and advanced patterns.
WebSocket Proxying
Enable WebSocket connections by upgrading HTTP protocol:
upstream websocket_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# Long timeouts for persistent connections
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
}Rate Limiting
Protect against abuse and DDoS attacks:
# In http context
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}
# In server context
server {
listen 80;
limit_req zone=api_limit burst=10 nodelay;
limit_conn conn_limit 10;
location /api/ {
proxy_pass http://backend;
}
}See references/security-hardening.md for complete security configuration.
Performance Optimization
Worker Configuration:
# In main context
user www-data;
worker_processes auto; # 1 per CPU core
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}Gzip Compression:
# In http context
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;Proxy Caching:
# Define cache zone
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=app_cache:100m
max_size=1g
inactive=60m;
# Use in location
location / {
proxy_cache app_cache;
proxy_cache_valid 200 60m;
proxy_cache_use_stale error timeout updating;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}See references/performance-tuning.md for detailed optimization strategies.
Security Headers
Add essential security headers to protect against common vulnerabilities:
# Create /etc/nginx/snippets/security-headers.conf
add_header Strict-Transport-Security "max-age=63072000; 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 "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;Include in server blocks:
server {
include snippets/security-headers.conf;
# ... rest of config
}Access Control
Restrict access by IP address:
server {
listen 80;
server_name admin.example.com;
# Allow specific IPs
allow 10.0.0.0/8;
allow 203.0.113.0/24;
# Deny all others
deny all;
location / {
proxy_pass http://admin_backend;
}
}Decision Framework
Choose nginx for: Performance-critical workloads (10K+ connections), reverse proxy, load balancing, static file serving, modern application stacks.
Choose alternatives for: Apache (.htaccess, mod_php, legacy apps), Caddy (auto-HTTPS, simpler config), Traefik (dynamic containers), Envoy (service mesh).
Safety Checklist
Before deploying nginx configurations:
- [ ] Test configuration syntax:
sudo nginx -t - [ ] Use reload, not restart:
sudo systemctl reload nginx(zero downtime) - [ ] Check error logs:
sudo tail -f /var/log/nginx/error.log - [ ] Verify SSL/TLS:
openssl s_client -connect domain:443 -servername domain - [ ] Test externally:
curl -I https://domain.com - [ ] Monitor worker processes:
ps aux | grep nginx - [ ] Check open connections:
netstat -an | grep :80 | wc -l - [ ] Verify backend health:
curl -I http://localhost:8080
Troubleshooting
Quick fixes: Test config (sudo nginx -t), check logs (/var/log/nginx/error.log), verify backend (curl http://127.0.0.1:3000).
Common errors: 502 (backend down), 504 (timeout - increase proxy_read_timeout), 413 (upload size - set client_max_body_size).
See references/troubleshooting.md for complete debugging guide.
Integration Points
Related Skills:
- implementing-tls - Certificate generation and automation (Let's Encrypt, cert-manager)
- load-balancing-patterns - Advanced load balancing architecture and decision frameworks
- deploying-applications - Application deployment strategies with nginx integration
- security-hardening - Complete server security beyond nginx-specific configuration
- configuring-firewalls - Firewall rules for HTTP/HTTPS access
- dns-management - DNS configuration for nginx virtual hosts
- kubernetes-operations - nginx Ingress Controller for Kubernetes
Additional Resources
Progressive Disclosure:
references/installation-guide.md- Detailed installation for all platformsreferences/configuration-structure.md- Complete nginx.conf anatomyreferences/static-sites.md- Static hosting patterns (basic, SPA, PHP)references/reverse-proxy.md- Advanced proxy scenarios and API gateway patternsreferences/load-balancing.md- All algorithms, health checks, sticky sessionsreferences/ssl-tls-config.md- Complete TLS configuration and certificate setupreferences/performance-tuning.md- Workers, caching, compression, buffersreferences/security-hardening.md- Rate limiting, headers, access controlreferences/troubleshooting.md- Common errors and debugging techniques
Working Examples:
examples/static-site/- Static website and SPA configurationsexamples/reverse-proxy/- Node.js, WebSocket, API gateway examplesexamples/load-balancing/- All load balancing algorithmsexamples/ssl-tls/- Modern TLS and mTLS configurationsexamples/performance/- High-traffic optimization and cachingexamples/security/- Rate limiting and security hardening
Reusable Snippets:
snippets/ssl-modern.conf- Modern TLS configurationsnippets/proxy-params.conf- Standard proxy headerssnippets/security-headers.conf- OWASP security headerssnippets/cache-static.conf- Static asset caching
# Round Robin Load Balancing (Default)
# Distributes requests sequentially across backend servers
upstream backend {
# Round-robin is default (no directive needed)
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
# Health checks (passive, open source)
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backend3.example.com:8080 max_fails=3 fail_timeout=30s;
# Backup server (only used if all others fail)
server backup.example.com:8080 backup;
# Persistent connections to backends
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://backend;
# 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;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Retry on errors
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
}
# Node.js Application Reverse Proxy
# Proxies requests to Node.js/Express backend
upstream nodejs_backend {
server 127.0.0.1:3000;
# For multiple instances (load balancing):
# server 127.0.0.1:3000;
# server 127.0.0.1:3001;
# server 127.0.0.1:3002;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name app.example.com;
# Logging
access_log /var/log/nginx/app.access.log;
error_log /var/log/nginx/app.error.log;
# Main application proxy
location / {
proxy_pass http://nodejs_backend;
# Preserve client information
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;
# HTTP/1.1 for keepalive connections
proxy_http_version 1.1;
proxy_set_header Connection "";
# Timeouts (adjust based on application)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# Serve static files directly (bypass Node.js)
location /static/ {
alias /var/www/app/static/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Health check endpoint (nginx responds directly)
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
# WebSocket Proxy Configuration
# For real-time applications (Socket.io, native WebSockets)
upstream websocket_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
listen [::]:80;
server_name ws.example.com;
location / {
proxy_pass http://websocket_backend;
# WebSocket upgrade headers (required)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 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;
# Long timeouts for persistent WebSocket connections
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
# Disable buffering for real-time data
proxy_buffering off;
}
}
# Basic Static Website Configuration
# Serves HTML, CSS, JS files from /var/www/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;
# Main location - serve files or return 404
location / {
try_files $uri $uri/ =404;
}
# Cache static assets for 1 year
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 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;
# Deny access to hidden files (.git, .htaccess, etc.)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Custom error pages (optional)
error_page 404 /404.html;
location = /404.html {
internal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
internal;
}
}
# Enable with:
# sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx
# Single Page Application Configuration
# For React, Vue, Angular apps with client-side routing
server {
listen 80;
listen [::]:80;
server_name app.example.com;
root /var/www/app/dist;
index index.html;
# Try file, then directory, then fallback to index.html for client-side routing
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets aggressively (hashed filenames)
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Don't cache index.html (app entrypoint)
location = /index.html {
add_header Cache-Control "no-cache, must-revalidate";
expires 0;
}
# Gzip compression for text assets
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# 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;
# Deny access to source maps in production (optional)
location ~ \.map$ {
deny all;
access_log off;
log_not_found off;
}
}
skill: "configuring-nginx"
version: "1.0"
domain: "infrastructure"
base_outputs:
# Core nginx configuration files ALWAYS produced
- path: "nginx/nginx.conf"
must_contain: ["events", "http", "worker_processes"]
description: "Main nginx configuration file with global settings"
- path: "nginx/conf.d/default.conf"
must_contain: ["server", "listen", "server_name"]
description: "Default server configuration"
conditional_outputs:
maturity:
starter:
- path: "nginx/sites-available/site.conf"
description: "Basic site configuration for static files or simple reverse proxy"
- path: "nginx/snippets/proxy-params.conf"
description: "Reusable proxy header configuration"
intermediate:
- path: "nginx/sites-available/site.conf"
must_contain: ["upstream", "proxy_pass"]
description: "Reverse proxy with load balancing configuration"
- path: "nginx/ssl/ssl-params.conf"
must_contain: ["ssl_protocols", "ssl_prefer_server_ciphers"]
description: "Modern TLS configuration"
- path: "nginx/snippets/security-headers.conf"
must_contain: ["add_header", "Strict-Transport-Security"]
description: "Security headers (HSTS, X-Frame-Options, CSP)"
- path: "nginx/snippets/cache-static.conf"
description: "Static asset caching configuration"
advanced:
- path: "nginx/sites-available/site.conf"
must_contain: ["upstream", "proxy_cache", "limit_req_zone"]
description: "Advanced proxy with caching and rate limiting"
- path: "nginx/ssl/ssl-params.conf"
must_contain: ["ssl_protocols TLSv1.3", "ssl_session_cache"]
description: "Advanced TLS 1.3 configuration with session caching"
- path: "nginx/snippets/rate-limit.conf"
must_contain: ["limit_req_zone", "limit_conn_zone"]
description: "Rate limiting and connection limiting configuration"
- path: "nginx/snippets/security-headers.conf"
must_contain: ["Content-Security-Policy", "Strict-Transport-Security"]
description: "Comprehensive security headers including CSP"
- path: "nginx/upstreams.conf"
must_contain: ["upstream", "keepalive"]
description: "Centralized upstream backend definitions"
infrastructure:
docker_compose:
- path: "docker-compose.yml"
must_contain: ["nginx:", "image: nginx"]
description: "Docker compose service definition for nginx"
- path: "nginx/Dockerfile"
description: "Custom nginx Docker image (if needed)"
kubernetes:
- path: "k8s/nginx-configmap.yaml"
must_contain: ["kind: ConfigMap", "nginx.conf"]
description: "ConfigMap for nginx configuration"
- path: "k8s/nginx-deployment.yaml"
must_contain: ["kind: Deployment", "image: nginx"]
description: "Nginx deployment manifest"
- path: "k8s/nginx-service.yaml"
must_contain: ["kind: Service", "port: 80"]
description: "Service exposing nginx"
- path: "k8s/nginx-ingress.yaml"
must_contain: ["kind: Ingress"]
description: "Ingress resource using nginx ingress controller"
bare_metal:
- path: "scripts/install-nginx.sh"
must_contain: ["apt install nginx", "systemctl enable nginx"]
description: "Installation script for Ubuntu/Debian"
- path: "nginx/sites-available/site.conf"
description: "Site configuration file"
- path: "scripts/enable-site.sh"
must_contain: ["ln -s", "nginx -t", "systemctl reload"]
description: "Script to enable site configuration"
use_case:
static_site:
- path: "nginx/sites-available/static-site.conf"
must_contain: ["root", "try_files", "location ~* \\.(jpg|jpeg|png|gif|css|js)"]
description: "Static file serving with cache headers"
reverse_proxy:
- path: "nginx/sites-available/reverse-proxy.conf"
must_contain: ["upstream", "proxy_pass", "proxy_set_header"]
description: "Reverse proxy configuration with proper headers"
- path: "nginx/snippets/proxy-params.conf"
must_contain: ["X-Real-IP", "X-Forwarded-For", "X-Forwarded-Proto"]
description: "Standard proxy headers snippet"
load_balancer:
- path: "nginx/sites-available/load-balancer.conf"
must_contain: ["upstream", "server .* weight", "keepalive"]
description: "Load balancing configuration with health checks"
- path: "nginx/upstreams.conf"
must_contain: ["max_fails", "fail_timeout"]
description: "Upstream backend pool with health monitoring"
websocket:
- path: "nginx/sites-available/websocket.conf"
must_contain: ["Upgrade", "Connection.*upgrade", "proxy_http_version 1.1"]
description: "WebSocket proxy configuration"
ssl_termination:
- path: "nginx/sites-available/ssl-site.conf"
must_contain: ["listen 443 ssl", "ssl_certificate", "ssl_certificate_key"]
description: "HTTPS configuration with SSL/TLS"
- path: "nginx/snippets/ssl-modern.conf"
must_contain: ["ssl_protocols TLSv1.3 TLSv1.2", "ssl_session_cache"]
description: "Modern TLS configuration snippet"
- path: "scripts/setup-letsencrypt.sh"
description: "Script to obtain Let's Encrypt certificates"
cloud_provider:
aws:
- path: "terraform/nginx-ec2.tf"
description: "Terraform configuration for nginx on EC2"
- path: "terraform/nginx-alb.tf"
description: "Application Load Balancer configuration"
- path: "scripts/userdata.sh"
must_contain: ["apt install nginx", "aws s3 cp"]
description: "EC2 user data script for nginx setup"
gcp:
- path: "terraform/nginx-gce.tf"
description: "Terraform configuration for nginx on GCE"
- path: "terraform/nginx-lb.tf"
description: "GCP Load Balancer configuration"
azure:
- path: "terraform/nginx-vm.tf"
description: "Terraform configuration for nginx on Azure VM"
- path: "terraform/nginx-appgw.tf"
description: "Azure Application Gateway configuration"
scaffolding:
- path: "nginx/sites-available/"
reason: "Directory for available nginx site configurations"
- path: "nginx/sites-enabled/"
reason: "Directory for enabled sites (symlinks to sites-available)"
- path: "nginx/snippets/"
reason: "Directory for reusable configuration snippets"
- path: "nginx/ssl/"
reason: "Directory for SSL/TLS certificates and configuration"
- path: "nginx/conf.d/"
reason: "Additional configuration files loaded by nginx"
- path: "scripts/"
reason: "Helper scripts for nginx management and deployment"
metadata:
primary_blueprints: ["api-first", "infrastructure"]
contributes_to:
- "Nginx configuration with modern security practices"
- "Reverse proxy setup for application backends"
- "Load balancing across multiple servers"
- "SSL/TLS termination with TLS 1.3 support"
- "Static file serving with caching"
- "WebSocket proxying"
- "Rate limiting and DDoS protection"
- "Security headers and CORS configuration"
- "Performance optimization (gzip, caching, keepalive)"
common_patterns:
- pattern: "Static website hosting"
files: ["nginx/sites-available/static-site.conf", "nginx/snippets/cache-static.conf"]
- pattern: "API reverse proxy"
files: ["nginx/sites-available/reverse-proxy.conf", "nginx/snippets/proxy-params.conf", "nginx/snippets/security-headers.conf"]
- pattern: "Load balanced application"
files: ["nginx/sites-available/load-balancer.conf", "nginx/upstreams.conf", "nginx/snippets/proxy-params.conf"]
- pattern: "HTTPS termination"
files: ["nginx/sites-available/ssl-site.conf", "nginx/snippets/ssl-modern.conf", "nginx/snippets/security-headers.conf"]
- pattern: "High-traffic production"
files: ["nginx/nginx.conf", "nginx/sites-available/site.conf", "nginx/snippets/rate-limit.conf", "nginx/upstreams.conf"]
validation:
required_checks:
- command: "nginx -t"
description: "Validate nginx configuration syntax"
must_succeed: true
- pattern: "listen (80|443)"
files: ["nginx/sites-available/*.conf"]
description: "Ensure server blocks have listen directive"
- pattern: "server_name"
files: ["nginx/sites-available/*.conf"]
description: "Ensure server blocks have server_name directive"
security_checks:
- pattern: "ssl_protocols.*TLSv1\\.3"
files: ["nginx/snippets/ssl-*.conf", "nginx/sites-available/*ssl*.conf"]
description: "Ensure modern TLS 1.3 is enabled"
required_when: "ssl_termination"
- pattern: "Strict-Transport-Security"
files: ["nginx/snippets/security-headers.conf"]
description: "Ensure HSTS header is configured"
required_when: "ssl_termination"
- pattern: "proxy_set_header X-Forwarded-Proto"
files: ["nginx/snippets/proxy-params.conf", "nginx/sites-available/*proxy*.conf"]
description: "Ensure forwarded protocol header is set"
required_when: "reverse_proxy"
nginx Configuration Structure
Complete guide to nginx configuration file organization and syntax.
Table of Contents
1. Configuration File Hierarchy 2. Context Levels 3. Configuration Directives 4. Include System 5. Variables 6. Best Practices
Configuration File Hierarchy
Standard Directory Structure
/etc/nginx/
├── nginx.conf # Main configuration file
├── mime.types # MIME type mappings
├── fastcgi.conf # FastCGI parameters
├── fastcgi_params # FastCGI parameters (legacy)
├── scgi_params # SCGI parameters
├── uwsgi_params # uWSGI parameters
├── modules-enabled/ # Enabled modules (symlinks)
│ └── 50-mod-http-geoip2.conf
├── modules-available/ # Available modules
│ └── mod-http-geoip2.conf
├── conf.d/ # Additional configs (*.conf)
│ └── custom.conf
├── sites-enabled/ # Enabled sites (Debian/Ubuntu)
│ └── default -> ../sites-available/default
├── sites-available/ # Available site configs
│ ├── default
│ ├── example.com.conf
│ └── api.example.com.conf
└── snippets/ # Reusable config snippets
├── ssl-params.conf
├── proxy-params.conf
└── security-headers.confNaming Conventions
Site configuration files:
- Use domain name:
example.com.conf - Include purpose:
api.example.com.conf - Avoid spaces: Use hyphens
my-site.conf
Snippets:
- Descriptive names:
ssl-modern.conf,proxy-params.conf - Purpose-based:
security-headers.conf,cache-static.conf
Context Levels
nginx configuration uses nested contexts (blocks). Directives in outer contexts are inherited by inner contexts.
Main Context (Global)
Top-level directives outside any block. Affect entire nginx instance.
# User and group
user www-data;
# Worker processes (auto = 1 per CPU core)
worker_processes auto;
# Maximum open files per worker
worker_rlimit_nofile 65535;
# Error log
error_log /var/log/nginx/error.log warn;
# Process ID file
pid /run/nginx.pid;
# Include dynamic modules
include /etc/nginx/modules-enabled/*.conf;Events Context
Connection processing configuration. Single events block in main context.
events {
# Maximum connections per worker
worker_connections 4096;
# Connection processing method (Linux: epoll, BSD: kqueue)
use epoll;
# Accept multiple connections at once
multi_accept on;
# Accept mutex for connection distribution
accept_mutex off;
}HTTP Context
HTTP-specific settings. Single http block in main context.
http {
# MIME types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging format
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;
# Performance settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# Gzip compression
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json;
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# Upstream definitions
upstream backend {
server 127.0.0.1:8080;
}
# Include server blocks
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Server Context (Virtual Host)
Defines a virtual host. Multiple server blocks allowed in http context.
server {
# Listen directives
listen 80;
listen [::]:80;
# Server name (virtual host matching)
server_name example.com www.example.com;
# Document root
root /var/www/example.com;
# Default index files
index index.html index.htm;
# Access log (optional, overrides http-level)
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# Server-specific settings
client_max_body_size 10m;
# Location blocks
location / {
try_files $uri $uri/ =404;
}
}Location Context (URL Routing)
URL-specific configuration. Multiple location blocks in server context.
# Exact match
location = /api/status {
return 200 "OK\n";
add_header Content-Type text/plain;
}
# Prefix match with regex stop
location ^~ /static/ {
root /var/www;
expires 1y;
}
# Regex match (case-sensitive)
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Regex match (case-insensitive)
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
access_log off;
}
# Prefix match (lowest priority)
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
# Named location (internal redirect only)
location @fallback {
proxy_pass http://backup_backend;
}Upstream Context
Backend server definitions. Multiple upstream blocks in http context.
upstream backend {
# Load balancing method
least_conn;
# Backend servers
server backend1.example.com:8080 weight=3;
server backend2.example.com:8080 weight=2;
server backend3.example.com:8080 backup;
# Health checks
server backend4.example.com:8080 max_fails=3 fail_timeout=30s;
# Persistent connections
keepalive 32;
keepalive_timeout 60s;
keepalive_requests 100;
}If Context
Conditional configuration. Use sparingly (can be inefficient).
# Set variable based on condition
set $mobile_redirect 0;
if ($http_user_agent ~* (mobile|android|iphone)) {
set $mobile_redirect 1;
}
if ($mobile_redirect = 1) {
return 302 https://m.example.com$request_uri;
}Warning: if in location context has limitations. Prefer map or try_files when possible.
Configuration Directives
Inheritance Rules
Directives in outer contexts are inherited by inner contexts unless overridden:
http {
# Applies to all servers
client_max_body_size 10m;
server {
# Inherits 10m from http
location /uploads {
# Override for this location
client_max_body_size 100m;
}
}
}Directive Types
Simple directives:
worker_processes 4;
server_tokens off;Block directives:
server {
# ...
}
location / {
# ...
}Array directives:
listen 80;
listen [::]:80;
listen 443 ssl http2;Include System
Modularize configuration using includes:
Including Files
# Include single file
include /etc/nginx/mime.types;
# Include all files matching pattern
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
# Include relative to nginx prefix
include conf.d/*.conf;Creating Reusable Snippets
Example: `/etc/nginx/snippets/proxy-params.conf`
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 Connection "";Usage:
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
}Site Management (Debian/Ubuntu)
Enable/disable sites without editing main config:
# Create site configuration
sudo nano /etc/nginx/sites-available/example.com
# Enable site (create symlink)
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# Disable site (remove symlink)
sudo rm /etc/nginx/sites-enabled/example.com
# Test and reload
sudo nginx -t && sudo systemctl reload nginxVariables
Built-in Variables
Request variables:
$request_uri # Full original request URI
$uri # Normalized URI
$request_method # GET, POST, etc.
$args # Query string
$arg_name # Specific query parameter
$is_args # "?" if query string exists
$request_body # Request body
$content_type # Content-Type header
$content_length # Content-Length headerConnection variables:
$remote_addr # Client IP address
$remote_port # Client port
$remote_user # HTTP auth username
$server_addr # Server IP address
$server_port # Server port
$server_protocol # HTTP/1.0, HTTP/1.1, HTTP/2.0Response variables:
$status # Response status code
$body_bytes_sent # Bytes sent to client
$bytes_sent # Total bytes sent (headers + body)
$connection # Connection serial number
$connection_requests # Request count in connection
$msec # Current Unix time
$request_time # Request processing time
$upstream_response_time # Time to receive upstream responseHeaders:
$http_user_agent # User-Agent header
$http_referer # Referer header
$http_host # Host header
$http_cookie # Cookie header
$http_x_forwarded_for # X-Forwarded-For headerSSL/TLS:
$ssl_protocol # TLS protocol version
$ssl_cipher # Cipher suite
$ssl_client_cert # Client certificate
$ssl_client_s_dn # Client DN
$ssl_session_id # SSL session IDCustom Variables
Define custom variables using set:
server {
# Set variable
set $backend_server backend1.example.com;
# Use variable
proxy_pass http://$backend_server;
}Map Directive
Create variables based on other variables:
http {
# Map user agent to backend
map $http_user_agent $backend {
default backend_web;
~*mobile backend_mobile;
~*bot backend_bot;
}
server {
location / {
proxy_pass http://$backend;
}
}
}Best Practices
Organization
1. Keep main nginx.conf minimal - Include only global settings 2. Use sites-available/sites-enabled pattern - Easy site management 3. Create reusable snippets - DRY principle 4. One site per file - Better organization 5. Use descriptive filenames - api.example.com.conf, not site1.conf
Comments
# Single-line comment
server {
listen 80; # Inline comment
# Multi-line explanation
# This location handles API requests
# and proxies to the backend
location /api/ {
proxy_pass http://backend;
}
}Testing
Always test before reloading:
# Test configuration syntax
sudo nginx -t
# View complete configuration (includes resolved)
sudo nginx -T
# Reload if test passes
sudo nginx -t && sudo systemctl reload nginxVersion Control
Track configuration in git:
cd /etc/nginx
git init
git add .
git commit -m "Initial nginx configuration"
# After changes
git diff
git add sites-available/new-site.conf
git commit -m "Add new-site configuration"Security
1. Hide nginx version: server_tokens off; 2. Set appropriate permissions:
sudo chown -R root:root /etc/nginx
sudo chmod -R 644 /etc/nginx
sudo chmod 755 /etc/nginx /etc/nginx/sites-available /etc/nginx/sites-enabled3. Protect sensitive files:
sudo chmod 600 /etc/nginx/ssl/*.keyPerformance
1. Use worker_processes auto - Automatic CPU core detection 2. Tune worker_connections - Based on expected traffic 3. Enable gzip compression - Reduce bandwidth 4. Use proxy caching - Reduce backend load 5. Minimize includes - Reduces config parsing time
Example: Complete Configuration Structure
# /etc/nginx/nginx.conf (main)
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
}
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"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
include snippets/security-headers.conf;
}Enable and test:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxnginx Installation Guide
Detailed installation instructions for nginx across different platforms.
Table of Contents
1. Ubuntu/Debian Installation 2. RHEL/CentOS/Rocky Installation 3. macOS Installation 4. Docker Installation 5. Building from Source 6. Post-Installation
Ubuntu/Debian Installation
Standard Installation
# Update package list
sudo apt update
# Install nginx
sudo apt install nginx -y
# Enable nginx to start on boot
sudo systemctl enable nginx
# Start nginx
sudo systemctl start nginx
# Check status
sudo systemctl status nginxLatest Stable from Official Repository
# Install prerequisites
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
# Import nginx signing key
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
# Add nginx stable repository
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
# Update and install
sudo apt update
sudo apt install nginxVerify Installation
nginx -v
# nginx version: nginx/1.24.0
curl http://localhost
# Should return nginx welcome pageRHEL/CentOS/Rocky Installation
Standard Installation
# Install nginx
sudo dnf install nginx -y
# Enable and start
sudo systemctl enable nginx
sudo systemctl start nginx
# Configure firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadFrom Official Repository
# Create repo file
sudo tee /etc/yum.repos.d/nginx.repo <<EOF
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF
# Install
sudo dnf install nginx -ymacOS Installation
Using Homebrew
# Install nginx
brew install nginx
# Start nginx
brew services start nginx
# Configuration location
ls -la /usr/local/etc/nginx/
# Default document root
ls -la /usr/local/var/www/Manual Start/Stop
# Start
nginx
# Stop
nginx -s stop
# Reload
nginx -s reloadDocker Installation
Basic Container
# Run nginx container
docker run -d \
--name nginx-server \
-p 80:80 \
nginx:alpine
# With custom config
docker run -d \
--name nginx-server \
-p 80:80 \
-v /path/to/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /path/to/html:/usr/share/nginx/html:ro \
nginx:alpineDocker Compose
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d:ro
- ./html:/usr/share/nginx/html:ro
- ./ssl:/etc/nginx/ssl:ro
restart: unless-stoppedBuilding from Source
Build nginx with custom modules:
# Install dependencies (Ubuntu/Debian)
sudo apt install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev \
libssl-dev libgd-dev libgeoip-dev
# Download nginx
wget http://nginx.org/download/nginx-1.24.0.tar.gz
tar -xzf nginx-1.24.0.tar.gz
cd nginx-1.24.0
# Configure with modules
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_v2_module \
--with-threads \
--with-stream \
--with-stream_ssl_module
# Compile and install
make
sudo make install
# Create nginx user
sudo useradd -r -M -s /sbin/nologin nginx
# Create cache directories
sudo mkdir -p /var/cache/nginx/{client_temp,proxy_temp}
sudo chown -R nginx:nginx /var/cache/nginxPost-Installation
File Locations
Configuration:
/etc/nginx/nginx.conf- Main configuration/etc/nginx/conf.d/- Additional configs/etc/nginx/sites-available/- Available sites (Debian/Ubuntu)/etc/nginx/sites-enabled/- Enabled sites (Debian/Ubuntu)
Logs:
/var/log/nginx/access.log- Access logs/var/log/nginx/error.log- Error logs
Web Root:
/usr/share/nginx/html/- Default (RHEL)/var/www/html/- Default (Debian/Ubuntu)
Initial Configuration Test
# Test configuration
sudo nginx -t
# View configuration
sudo nginx -T
# Check version and modules
nginx -VService Management
# Start nginx
sudo systemctl start nginx
# Stop nginx
sudo systemctl stop nginx
# Reload configuration (zero downtime)
sudo systemctl reload nginx
# Restart nginx
sudo systemctl restart nginx
# Check status
sudo systemctl status nginx
# View logs
sudo journalctl -u nginx -fFirewall Configuration
UFW (Ubuntu/Debian):
sudo ufw allow 'Nginx Full'
# Or individually:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpfirewalld (RHEL/CentOS):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadSELinux Configuration (RHEL/CentOS)
# Allow nginx to make network connections
sudo setsebool -P httpd_can_network_connect 1
# Allow nginx to serve files from custom directory
sudo chcon -R -t httpd_sys_content_t /path/to/webroot/Verification
Test nginx is working:
# Check nginx is running
ps aux | grep nginx
# Test HTTP locally
curl http://localhost
# Test from external machine
curl http://your-server-ip
# Check listening ports
sudo netstat -tlnp | grep nginx
# OR
sudo ss -tlnp | grep nginxNext Steps
After installation: 1. Configure firewall (see above) 2. Set up virtual hosts (see static-sites.md) 3. Configure SSL/TLS (see ssl-tls-config.md) 4. Optimize performance (see performance-tuning.md) 5. Implement security hardening (see security-hardening.md)
nginx Load Balancing
Guide to load balancing configuration in nginx.
Table of Contents
- Load Balancing Methods
- Round Robin (Default)
- Least Connections
- IP Hash (Sticky Sessions)
- Weighted Load Balancing
- Health Checks
- Passive Health Checks (Open Source)
- Persistent Connections
- Keepalive to Backend
- Complete Examples
- High Availability Setup
Load Balancing Methods
Round Robin (Default)
Sequential distribution to each server:
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}Least Connections
Route to server with fewest active connections:
upstream backend {
least_conn;
server backend1.example.com:8080;
server backend2.example.com:8080;
}IP Hash (Sticky Sessions)
Same client always routes to same server based on IP:
upstream backend {
ip_hash;
server backend1.example.com:8080;
server backend2.example.com:8080;
}Note: Can cause uneven distribution if many clients behind NAT.
Weighted Load Balancing
Distribute based on server capacity:
upstream backend {
server backend1.example.com:8080 weight=3; # Gets 3x traffic
server backend2.example.com:8080 weight=2; # Gets 2x traffic
server backend3.example.com:8080 weight=1; # Gets 1x traffic
}Health Checks
Passive Health Checks (Open Source)
nginx marks server down after failures:
upstream backend {
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backup.example.com:8080 backup;
}max_fails: Number of failed attempts before marking downfail_timeout: How long to wait before retrybackup: Only used if all primary servers down
Persistent Connections
Keepalive to Backend
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
keepalive 32; # Keep 32 idle connections open
keepalive_timeout 60s;
keepalive_requests 100;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}Complete Examples
High Availability Setup
upstream backend {
least_conn;
# Primary servers
server backend1.example.com:8080 weight=3 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 weight=2 max_fails=3 fail_timeout=30s;
# Backup server
server backup.example.com:8080 backup;
# Persistent connections
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
include snippets/proxy-params.conf;
}
}nginx Performance Tuning
Optimization strategies for high-traffic nginx deployments.
Table of Contents
- Worker Process Configuration
- CPU and Connection Optimization
- Check System Limits
- HTTP Performance Settings
- Gzip Compression
- File Caching
- Open File Cache
- Static Asset Caching
- Proxy Caching
- Buffering
- Proxy Buffering
- Connection Keep-Alive
- Client Connections
- Upstream Connections
- Monitoring Performance
- Status Module
- Benchmarking
- Using ab (ApacheBench)
- Using wrk
- Best Practices
Worker Process Configuration
CPU and Connection Optimization
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto; # 1 per CPU core
worker_rlimit_nofile 65535; # Match system ulimit
pid /run/nginx.pid;
events {
worker_connections 4096; # Max connections per worker
use epoll; # Linux: epoll, BSD: kqueue, macOS: kqueue
multi_accept on; # Accept multiple connections at once
}Calculations:
- Max clients = worker_processes × worker_connections
- With 4 cores and 4096 connections: 16,384 concurrent connections
Check System Limits
# Check current limit
ulimit -n
# Set limit temporarily
ulimit -n 65535
# Set permanently in /etc/security/limits.conf
www-data soft nofile 65535
www-data hard nofile 65535HTTP Performance Settings
http {
# File operations
sendfile on; # Kernel-level file sending
tcp_nopush on; # Send headers in one packet
tcp_nodelay on; # Don't buffer data
# Timeouts
keepalive_timeout 65;
keepalive_requests 100;
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
# Hide version
server_tokens off;
# Hash table sizes
types_hash_max_size 2048;
server_names_hash_bucket_size 64;
# Buffer sizes
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
output_buffers 2 32k;
}Gzip Compression
Reduce bandwidth and improve page load times:
http {
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # 1-9, balance compression vs CPU
gzip_min_length 1024; # Don't compress < 1KB
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Don't compress already-compressed formats
gzip_disable "msie6";
}File Caching
Open File Cache
Cache file descriptors:
http {
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}Static Asset Caching
Browser caching for static files:
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}Proxy Caching
Cache backend responses:
http {
# Define cache zone
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=app_cache:100m
max_size=1g
inactive=60m
use_temp_path=off;
server {
location / {
proxy_pass http://backend;
# Enable caching
proxy_cache app_cache;
proxy_cache_valid 200 60m;
proxy_cache_valid 404 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
# Cache status header
add_header X-Cache-Status $upstream_cache_status;
}
}
}Buffering
Proxy Buffering
location / {
proxy_pass http://backend;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}Connection Keep-Alive
Client Connections
keepalive_timeout 65;
keepalive_requests 100;Upstream Connections
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
keepalive 32; # Keep 32 idle connections
keepalive_timeout 60s;
keepalive_requests 100;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}Monitoring Performance
Status Module
Enable stub_status module:
server {
listen 8080;
server_name localhost;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}Check status:
curl http://localhost:8080/nginx_statusOutput:
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106Benchmarking
Using ab (ApacheBench)
# 1000 requests, 10 concurrent
ab -n 1000 -c 10 http://example.com/
# With keepalive
ab -n 1000 -c 10 -k http://example.com/Using wrk
# 12 threads, 400 connections, 30 seconds
wrk -t12 -c400 -d30s http://example.com/Best Practices
1. Use worker_processes auto - Automatically matches CPU cores 2. Enable gzip compression - Reduces bandwidth 3. Cache static assets - Long expires headers 4. Use proxy caching - Reduces backend load 5. Enable keepalive to upstreams - Reduces connection overhead 6. Monitor worker connections - Increase if hitting limits 7. Tune buffer sizes - Based on typical request/response sizes 8. Log analysis - Identify slow endpoints
nginx Reverse Proxy Configuration
Complete guide to configuring nginx as a reverse proxy for backend applications.
Table of Contents
1. Basic Reverse Proxy 2. Proxy Headers 3. WebSocket Proxying 4. API Gateway Patterns 5. Proxy Buffering 6. Timeouts 7. Error Handling
Basic Reverse Proxy
Single Backend
Proxy all requests to a single backend server:
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_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;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}Multiple Backends (Load Balancing)
upstream app_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 32;
}Proxy Headers
Essential Headers
Create /etc/nginx/snippets/proxy-params.conf:
# Preserve client information
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;
# HTTP/1.1 for keepalive
proxy_http_version 1.1;
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;Usage:
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
}Header Explanations
Host: Backend needs original hostname for virtual host routing
proxy_set_header Host $host;X-Real-IP: Backend sees client IP, not proxy IP
proxy_set_header X-Real-IP $remote_addr;X-Forwarded-For: Full chain of proxies (append to existing)
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;X-Forwarded-Proto: Backend knows if original request was HTTPS
proxy_set_header X-Forwarded-Proto $scheme;Connection: Enable persistent connections to backend
proxy_http_version 1.1;
proxy_set_header Connection "";WebSocket Proxying
WebSocket requires HTTP/1.1 and connection upgrade:
upstream websocket_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name ws.example.com;
location / {
proxy_pass http://websocket_backend;
# WebSocket upgrade headers
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 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;
# Long timeouts for persistent connections
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
}Socket.io Configuration
location /socket.io/ {
proxy_pass http://socketio_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}API Gateway Patterns
Path-Based Routing
Route different paths to different services:
upstream auth_service {
server 127.0.0.1:4000;
}
upstream user_service {
server 127.0.0.1:4001;
}
upstream order_service {
server 127.0.0.1:4002;
}
server {
listen 80;
server_name api.example.com;
# Authentication service
location /api/auth/ {
proxy_pass http://auth_service/;
include snippets/proxy-params.conf;
}
# User service
location /api/users/ {
proxy_pass http://user_service/;
include snippets/proxy-params.conf;
}
# Order service
location /api/orders/ {
proxy_pass http://order_service/;
include snippets/proxy-params.conf;
}
# Health check (nginx responds directly)
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}Header-Based Routing
Route based on request headers:
http {
map $http_x_api_version $backend {
"v1" backend_v1;
"v2" backend_v2;
default backend_v2;
}
upstream backend_v1 {
server 127.0.0.1:4001;
}
upstream backend_v2 {
server 127.0.0.1:4002;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://$backend;
include snippets/proxy-params.conf;
}
}
}Subdomain-Based Routing
Route based on subdomain:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://api_backend;
include snippets/proxy-params.conf;
}
}
server {
listen 80;
server_name admin.example.com;
location / {
proxy_pass http://admin_backend;
include snippets/proxy-params.conf;
}
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app_backend;
include snippets/proxy-params.conf;
}
}Proxy Buffering
Enable Buffering (Default)
Reduces load on backend by buffering responses:
location / {
proxy_pass http://backend;
# Enable buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_max_temp_file_size 1024m;
}Disable Buffering (Streaming)
For server-sent events (SSE) or real-time streaming:
location /stream {
proxy_pass http://backend;
proxy_buffering off;
proxy_cache off;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Long timeout for streaming
proxy_read_timeout 24h;
}Request Body Buffering
# Buffer small uploads to disk
client_body_buffer_size 128k;
# Maximum upload size
client_max_body_size 100m;
# Temp file location
client_body_temp_path /var/nginx/client_body_temp;Timeouts
Proxy Timeouts
location / {
proxy_pass http://backend;
# Time to establish connection to backend
proxy_connect_timeout 60s;
# Time to transmit request to backend
proxy_send_timeout 60s;
# Time to receive response from backend
proxy_read_timeout 60s;
}Application-Specific Timeouts
Fast APIs:
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;Slow processing (reports, analytics):
proxy_connect_timeout 10s;
proxy_send_timeout 120s;
proxy_read_timeout 300s; # 5 minutesLong-polling:
proxy_connect_timeout 10s;
proxy_read_timeout 3600s; # 1 hourError Handling
Custom Error Pages
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
include snippets/proxy-params.conf;
# Intercept errors
proxy_intercept_errors on;
# Custom error pages
error_page 502 503 504 /50x.html;
error_page 404 /404.html;
}
location = /50x.html {
root /var/www/errors;
internal;
}
location = /404.html {
root /var/www/errors;
internal;
}
}Fallback to Backup Backend
upstream backend {
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backup.example.com:8080 backup;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
include snippets/proxy-params.conf;
}
}Named Location Fallback
location / {
proxy_pass http://primary_backend;
proxy_intercept_errors on;
error_page 502 503 504 = @fallback;
}
location @fallback {
proxy_pass http://backup_backend;
include snippets/proxy-params.conf;
}Advanced Patterns
Serving Static Files Directly
Bypass backend for static assets:
server {
listen 80;
server_name app.example.com;
# Serve static files directly from nginx
location /static/ {
alias /var/www/app/static/;
expires 1y;
access_log off;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /var/www/app/media/;
expires 30d;
}
# Proxy dynamic content to backend
location / {
proxy_pass http://app_backend;
include snippets/proxy-params.conf;
}
}URL Rewriting
Modify URL before proxying:
# Remove /api prefix before proxying
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://backend;
include snippets/proxy-params.conf;
}
# Add prefix
location / {
proxy_pass http://backend/app/;
include snippets/proxy-params.conf;
}Conditional Proxying
# Route mobile users to different backend
set $backend app_backend;
if ($http_user_agent ~* (mobile|android|iphone)) {
set $backend mobile_backend;
}
location / {
proxy_pass http://$backend;
include snippets/proxy-params.conf;
}Adding/Removing Headers
location / {
proxy_pass http://backend;
# Add custom headers
proxy_set_header X-Custom-Header "value";
proxy_set_header X-Request-ID $request_id;
# Remove headers from response
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
include snippets/proxy-params.conf;
}Testing and Debugging
Test Proxy Configuration
# Test nginx configuration
sudo nginx -t
# Reload nginx
sudo systemctl reload nginx
# Test locally
curl -H "Host: app.example.com" http://localhost/
# Check backend is reachable
curl http://127.0.0.1:3000/
# Test with headers
curl -H "X-Forwarded-For: 1.2.3.4" http://app.example.com/View Proxy Headers
Backend application should log received headers to verify proxy configuration:
// Node.js/Express
app.use((req, res, next) => {
console.log('Headers:', req.headers);
console.log('Host:', req.headers.host);
console.log('X-Real-IP:', req.headers['x-real-ip']);
console.log('X-Forwarded-For:', req.headers['x-forwarded-for']);
console.log('X-Forwarded-Proto:', req.headers['x-forwarded-proto']);
next();
});Debug Proxy Issues
Enable debug logging:
error_log /var/log/nginx/error.log debug;
location / {
proxy_pass http://backend;
access_log /var/log/nginx/proxy-debug.log;
}View logs:
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/proxy-debug.logCommon Issues
502 Bad Gateway:
- Backend not running:
curl http://127.0.0.1:3000 - Wrong backend address in proxy_pass
- SELinux blocking connections:
sudo setsebool -P httpd_can_network_connect 1
504 Gateway Timeout:
- Backend too slow
- Increase proxy_read_timeout
- Check backend performance
Connection reset:
- Ensure proxy_http_version 1.1
- Check keepalive settings
- Verify upstream keepalive directive
Headers not reaching backend:
- Check proxy_set_header directives
- Verify backend logging
- Test with curl -H
nginx Security Hardening
Security best practices for nginx deployments.
Table of Contents
- Rate Limiting
- Basic Rate Limiting
- Custom Error Response
- Security Headers
- Access Control
- IP-Based Restrictions
- Geo-Based Blocking
- Hide Server Information
- Disable Unwanted HTTP Methods
- Prevent Hotlinking
- File Upload Security
- Protect Sensitive Files
- Block User Agents
- DDoS Protection
- Connection Limits
- Slowloris Protection
- SSL/TLS Security
- ModSecurity WAF (Optional)
- Security Checklist
Rate Limiting
Protect against DDoS and brute force attacks:
Basic Rate Limiting
http {
# Define rate limit zones
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=2r/m;
# Connection limit
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}
server {
listen 80;
# Global rate limit
limit_req zone=general_limit burst=20 nodelay;
limit_conn conn_limit 10;
location / {
proxy_pass http://backend;
}
# API with stricter limits
location /api/ {
limit_req zone=api_limit burst=10 nodelay;
proxy_pass http://backend;
}
# Login endpoint
location /login {
limit_req zone=login_limit burst=5 nodelay;
proxy_pass http://backend;
}
}Custom Error Response
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/s;
limit_req_status 429;
server {
location /api/ {
limit_req zone=api_limit burst=10 nodelay;
error_page 429 = @rate_limited;
proxy_pass http://backend;
}
location @rate_limited {
return 429 '{"error": "Rate limit exceeded", "retry_after": 60}\n';
add_header Content-Type application/json;
}
}Security Headers
OWASP recommended security headers:
Create /etc/nginx/snippets/security-headers.conf:
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS Protection
add_header X-XSS-Protection "1; mode=block" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (customize for your app)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self';" always;
# Permissions Policy
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;Usage:
server {
listen 443 ssl http2;
server_name example.com;
include snippets/security-headers.conf;
location / {
# ...
}
}Access Control
IP-Based Restrictions
server {
listen 80;
server_name admin.example.com;
# Allow specific IPs
allow 10.0.0.0/8;
allow 203.0.113.0/24;
# Deny all others
deny all;
location / {
proxy_pass http://admin_backend;
}
}Geo-Based Blocking
http {
# Requires geoip module
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allowed_country {
default no;
US yes;
CA yes;
GB yes;
}
server {
listen 80;
if ($allowed_country = no) {
return 403;
}
location / {
proxy_pass http://backend;
}
}
}Hide Server Information
http {
# Hide nginx version
server_tokens off;
# Remove Server header entirely (requires headers-more module)
more_clear_headers 'Server';
}Disable Unwanted HTTP Methods
server {
listen 80;
# Only allow GET, POST, HEAD
if ($request_method !~ ^(GET|POST|HEAD)$) {
return 405;
}
location / {
proxy_pass http://backend;
}
}Prevent Hotlinking
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked server_names
*.example.com example.com;
if ($invalid_referer) {
return 403;
}
}File Upload Security
server {
# Limit upload size
client_max_body_size 10m;
# Limit upload speed
limit_rate 500k;
# File upload location
location /upload {
# Only allow POST
if ($request_method != POST) {
return 405;
}
proxy_pass http://upload_backend;
proxy_set_header X-Real-IP $remote_addr;
}
}Protect Sensitive Files
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Deny access to backup files
location ~* \.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
}
# Protect specific directories
location ~* /(uploads|files)/.*\.(php|php5|php7|phtml)$ {
deny all;
}Block User Agents
# Block bad bots
if ($http_user_agent ~* (scrapy|curl|wget|python-requests)) {
return 403;
}
# Or use map
map $http_user_agent $bad_bot {
default 0;
~*scrapy 1;
~*curl 1;
~*wget 1;
}
server {
if ($bad_bot) {
return 403;
}
}DDoS Protection
Connection Limits
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location / {
limit_conn addr 10;
limit_req zone=one burst=5;
proxy_pass http://backend;
}
}
}Slowloris Protection
http {
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;
}SSL/TLS Security
See ssl-tls-config.md for complete TLS configuration.
ModSecurity WAF (Optional)
Web Application Firewall integration:
# Install ModSecurity
sudo apt install libmodsecurity3 libnginx-mod-http-modsecurity
# Enable in nginx
load_module modules/ngx_http_modsecurity_module.so;
http {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
}Security Checklist
- [ ] Enable SSL/TLS with modern configuration
- [ ] Add security headers
- [ ] Implement rate limiting
- [ ] Hide server version and information
- [ ] Restrict access by IP for admin panels
- [ ] Disable unwanted HTTP methods
- [ ] Protect sensitive files
- [ ] Limit file upload sizes
- [ ] Configure timeouts to prevent slowloris
- [ ] Regular security updates:
sudo apt update && sudo apt upgrade nginx - [ ] Monitor logs for suspicious activity
- [ ] Use fail2ban for automated blocking
SSL/TLS Configuration for nginx
Modern SSL/TLS configuration patterns.
Table of Contents
- Modern TLS Configuration (2025)
- Reusable SSL Snippet
- Let's Encrypt Integration
- Certbot Installation
- Obtain Certificate
- Auto-Renewal
- Client Certificate Authentication (mTLS)
- Testing SSL/TLS
Modern TLS Configuration (2025)
Recommended for compatibility and security:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
# Certificate files
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Protocols (TLS 1.2 and 1.3)
ssl_protocols TLSv1.3 TLSv1.2;
# Cipher suites
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# Session resumption
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
location / {
root /var/www/example.com;
index index.html;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$server_name$request_uri;
}Reusable SSL Snippet
Create /etc/nginx/snippets/ssl-modern.conf:
# Protocols
ssl_protocols TLSv1.3 TLSv1.2;
# Ciphers
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# Session resumption
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;Usage:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include snippets/ssl-modern.conf;
location / {
# ...
}
}Let's Encrypt Integration
Certbot Installation
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# RHEL/CentOS
sudo dnf install certbot python3-certbot-nginxObtain Certificate
# Automatic configuration
sudo certbot --nginx -d example.com -d www.example.com
# Manual certificate only
sudo certbot certonly --nginx -d example.com -d www.example.comAuto-Renewal
# Test renewal
sudo certbot renew --dry-run
# Automatic renewal (systemd timer)
sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timerClient Certificate Authentication (mTLS)
Mutual TLS for service-to-service authentication:
server {
listen 443 ssl http2;
server_name internal-api.example.com;
# Server certificate
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# CA certificate to verify client certs
ssl_client_certificate /etc/ssl/certs/ca.crt;
# Require valid client certificate
ssl_verify_client on;
ssl_verify_depth 2;
include snippets/ssl-modern.conf;
location / {
proxy_pass http://backend;
# Pass client cert info to backend
proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
include snippets/proxy-params.conf;
}
}Testing SSL/TLS
# Test SSL connection
openssl s_client -connect example.com:443 -servername example.com
# Check certificate
openssl s_client -connect example.com:443 -servername example.com < /dev/null | openssl x509 -text
# Test with curl
curl -vI https://example.com
# SSL Labs test
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=example.comStatic Site Configuration
nginx configuration patterns for static websites.
Table of Contents
Basic Static Website
Serve HTML/CSS/JS from a directory:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
}
}Single Page Application (SPA)
React, Vue, Angular apps with client-side routing:
server {
listen 80;
server_name app.example.com;
root /var/www/app/dist;
index index.html;
# Try file, directory, then fallback to index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets aggressively
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Don't cache index.html
location = /index.html {
add_header Cache-Control "no-cache, must-revalidate";
expires 0;
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}PHP Application (WordPress, Laravel)
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP processing
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to sensitive files
location ~ /\.(ht|git|env) {
deny all;
}
# Cache static files
location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml|woff2)$ {
expires 30d;
access_log off;
}
}nginx Troubleshooting Guide
Common issues and solutions.
Table of Contents
- Configuration Errors
- Test Configuration
- Common Syntax Errors
- Common HTTP Errors
- 502 Bad Gateway
- 503 Service Unavailable
- 504 Gateway Timeout
- 413 Request Entity Too Large
- 404 Not Found
- Port and Binding Issues
- "Address already in use"
- Permission Denied
- SSL/TLS Issues
- Certificate Not Found
- SSL Handshake Failure
- Performance Issues
- High Memory Usage
- High CPU Usage
- Connection Issues
- Connection Refused
- Connection Reset
- Logging and Debugging
- Enable Debug Logging
- View Logs
- Custom Log Format
- Service Management Issues
- nginx Not Starting
- Can't Reload Configuration
- Quick Diagnostics
Configuration Errors
Test Configuration
Always test before reloading:
# Test configuration
sudo nginx -t
# View complete configuration
sudo nginx -T
# Reload if test passes
sudo nginx -t && sudo systemctl reload nginxCommon Syntax Errors
Missing semicolon:
# Wrong
server {
listen 80
}
# Correct
server {
listen 80;
}Invalid directive:
# Check spelling and context
proxy_pass http://backend; # Correct
proxy-pass http://backend; # Wrong (dash instead of underscore)Common HTTP Errors
502 Bad Gateway
Backend server not reachable or not running.
Check backend:
# Is backend running?
curl http://127.0.0.1:3000
# Check process
ps aux | grep node # or python, ruby, etc.
# Check logs
sudo journalctl -u myapp -fFix SELinux (RHEL/CentOS):
# Allow nginx to connect to network
sudo setsebool -P httpd_can_network_connect 1503 Service Unavailable
All backend servers are down or unreachable.
Check upstream configuration:
upstream backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
}Reset failure count:
sudo systemctl reload nginx504 Gateway Timeout
Backend taking too long to respond.
Increase timeouts:
location / {
proxy_pass http://backend;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 300s; # 5 minutes
}413 Request Entity Too Large
File upload exceeds limit.
Increase upload size:
server {
client_max_body_size 100m;
}404 Not Found
File or location not found.
Check root path:
server {
root /var/www/example.com; # Verify path exists
location / {
try_files $uri $uri/ =404;
}
}Verify file permissions:
ls -la /var/www/example.com
# Should be readable by www-data userPort and Binding Issues
"Address already in use"
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)Find what's using the port:
sudo lsof -i :80
sudo netstat -tlnp | grep :80
sudo ss -tlnp | grep :80Stop conflicting service:
sudo systemctl stop apache2Permission Denied
nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)Port 80/443 require root privileges:
# Start nginx as root
sudo systemctl start nginx
# Or use non-privileged ports (>1024)
listen 8080;SSL/TLS Issues
Certificate Not Found
nginx: [emerg] cannot load certificate "/etc/ssl/certs/example.com.crt"Verify certificate exists:
sudo ls -la /etc/ssl/certs/example.com.crt
sudo ls -la /etc/ssl/private/example.com.keyCheck permissions:
sudo chmod 644 /etc/ssl/certs/example.com.crt
sudo chmod 600 /etc/ssl/private/example.com.keySSL Handshake Failure
Test SSL:
openssl s_client -connect example.com:443 -servername example.comCheck certificate chain:
openssl s_client -connect example.com:443 -showcertsPerformance Issues
High Memory Usage
Check worker configuration:
worker_processes auto; # Don't set too high
worker_connections 4096; # Adjust based on trafficMonitor processes:
top
htop
ps aux | grep nginxHigh CPU Usage
Check gzip compression level:
gzip_comp_level 6; # Don't use 9, too CPU intensiveMonitor with top:
top -p $(pgrep -d, nginx)Connection Issues
Connection Refused
Backend not accepting connections.
Check backend is listening:
netstat -tlnp | grep 3000
curl http://127.0.0.1:3000Connection Reset
Enable keepalive:
upstream backend {
server 127.0.0.1:3000;
keepalive 32;
}
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
}Logging and Debugging
Enable Debug Logging
error_log /var/log/nginx/error.log debug;Warning: Very verbose, use temporarily.
View Logs
# Error log
sudo tail -f /var/log/nginx/error.log
# Access log
sudo tail -f /var/log/nginx/access.log
# Systemd logs
sudo journalctl -u nginx -fCustom Log Format
log_format debug '$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"';
access_log /var/log/nginx/debug.log debug;Service Management Issues
nginx Not Starting
Check configuration:
sudo nginx -tCheck service status:
sudo systemctl status nginxView detailed errors:
sudo journalctl -xeu nginxCan't Reload Configuration
Force reload:
sudo systemctl reload nginx
# If that fails, restart
sudo systemctl restart nginxQuick Diagnostics
# 1. Test configuration
sudo nginx -t
# 2. Check nginx is running
sudo systemctl status nginx
# 3. Check ports
sudo netstat -tlnp | grep nginx
# 4. Test locally
curl http://localhost
# 5. Check logs
sudo tail -20 /var/log/nginx/error.log
# 6. Check backend
curl http://127.0.0.1:3000
# 7. Verify DNS
dig example.com
# 8. Test SSL
openssl s_client -connect example.com:443# Static Asset Caching
# Include with: include snippets/cache-static.conf;
# Cache static assets for 1 year (use with versioned/hashed filenames)
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(woff|woff2|ttf|eot|otf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Media files
location ~* \.(mp4|webm|ogg|mp3|wav)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# Standard Proxy Headers
# Include with: include snippets/proxy-params.conf;
# Preserve client information
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;
# HTTP/1.1 for keepalive connections
proxy_http_version 1.1;
proxy_set_header Connection "";
# Timeouts (adjust based on application)
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# OWASP Recommended Security Headers
# Include with: include snippets/security-headers.conf;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS Protection (legacy, but still useful)
add_header X-XSS-Protection "1; mode=block" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (customize for your application)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self';" always;
# Permissions Policy (formerly Feature-Policy)
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Modern SSL/TLS Configuration (2025)
# Include with: include snippets/ssl-modern.conf;
# Requires certificate and key to be set in server block
# Protocols (TLS 1.3 and 1.2)
ssl_protocols TLSv1.3 TLSv1.2;
# Cipher suites (Modern configuration)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
# Session resumption
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
Related skills
FAQ
What TLS version does it configure?
It uses TLSv1.3 and TLSv1.2 with HSTS (Strict-Transport-Security) headers for HTTPS.
Can it proxy WebSocket connections?
Yes, it covers reverse proxy and WebSocket proxying with proxy_http_version 1.1 and connection upgrade patterns.