NGINX - High Performance Web Server
Install and configure NGINX, the high-performance web server and reverse proxy powering much of the internet — covering installation, configuration, load balancing, SSL termination, and common use cases.
- Step 1
Overview
NGINX (pronounced 'engine-x') is a high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. It's designed for high concurrency, low memory usage, and high stability.
Key capabilities:
- Web Server: Serve static content with exceptional performance
- Reverse Proxy: Forward client requests to backend servers
- Load Balancer: Distribute traffic across multiple servers
- SSL/TLS Termination: Handle HTTPS encryption/decryption
- HTTP Cache: Cache backend responses to reduce load
- Web Application Firewall (WAF): Protect applications from attacks
Why NGINX:
- Performance: Event-driven architecture handles 10,000+ concurrent connections
- Reliability: Battle-tested on millions of websites
- Flexibility: Modular design with extensive configuration options
- Efficiency: Low memory footprint with high throughput
- Features: Built-in load balancing, caching, and compression
Official site: https://nginx.org GitHub: https://github.com/nginx/nginx (30K+ stars) Documentation: https://nginx.org/en/docs/ - Step 2
Quick Installation Options
Multiple installation methods available depending on your OS:
Recommended:
- Use OS package manager for production (stable, auto-updates)
- Use Docker for development/testing
- Build from source for custom modules or latest features
# Option 1: Ubuntu/Debian sudo apt update sudo apt install nginx sudo systemctl start nginx sudo systemctl enable nginx # Option 2: RHEL/CentOS/Fedora sudo dnf install nginx sudo systemctl start nginx sudo systemctl enable nginx # Option 3: macOS (Homebrew) brew install nginx brew services start nginx # Option 4: Docker docker run -d -p 80:80 -p 443:443 --name nginx nginx:latest # Option 5: Windows (download from nginx.org) # Extract to C:\nginx and run nginx.exe # Verify installation curl http://localhost nginx -v # Check status systemctl status nginx - Step 3
Build from Source
Build NGINX from source to include custom modules or get the latest features. This is useful when you need modules not available in your OS packages.
# Install dependencies sudo apt update sudo apt install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev \ openssl libssl-dev wget git # Download NGINX source cd /tmp wget http://nginx.org/download/nginx-1.26.2.tar.gz tar xzf nginx-1.26.2.tar.gz cd nginx-1.26.2 # Configure with common modules ./configure --prefix=/usr/local/nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_auth_request_module \ --with-http_addition_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_gzip_static_module \ --with_http_gunzip_module \ --with_http_secure_link_module \ --with-http_slice_module \ --with-http_stub_status_module \ --with-http_sub_module \ --with-mail \ --with-stream \ --with-stream_ssl_module # Build and install make sudo make install # Create init script wget http://nginx.org/resources/events/start -O /etc/init.d/nginx chmod +x /etc/init.d/nginx # Verify /usr/local/nginx/sbin/nginx -v # Optional: Add to PATH echo 'export PATH=/usr/local/nginx/sbin:$PATH' >> ~/.bashrc - Step 4
Directory Structure
Understanding NGINX's default directory structure helps with configuration and troubleshooting:
Key directories:
/etc/nginx/- Configuration files/var/www/- Default web root (varies by distro)/var/log/nginx/- Log files/usr/share/nginx/html/- Default web root on some distros
# Main configuration /etc/nginx/nginx.conf # Site configurations /etc/nginx/sites-available/ # Available site configs /etc/nginx/sites-enabled/ # Enabled sites (symlinks) /etc/nginx/conf.d/ # Additional config includes # Web root /var/www/html/ # Default document root /usr/share/nginx/html/ # Alternative default # Logs /var/log/nginx/access.log /var/log/nginx/error.log # Test configuration nginx -t # Reload configuration sudo systemctl reload nginx # Find your web root nginx -V 2>&1 | grep prefix - Step 5
Basic Configuration
NGINX configuration is divided into contexts: main, events, http, server, and location. Here's a basic but complete configuration:
Configuration hierarchy:
main └── events (worker connections) └── http (HTTP/HTTPS settings) └── server (virtual host) └── location (URI matching)Key directives:
worker_processes: Number of worker processesworker_connections: Max connections per workerlisten: Port and IP to listen onserver_name: Domain namesroot: Document root directorylocation: URI matching rules
# Main config: /etc/nginx/nginx.conf user www-data; worker_processes auto; worker_connections 1024; pid /run/nginx.pid; events { worker_connections 1024; use epoll; multi_accept on; } http { # Basic settings include /etc/nginx/mime.types; default_type application/octet-stream; # Logging 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; error_log /var/log/nginx/error.log; # Performance sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # Gzip compression gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css text/xml application/json application/javascript application/xml; # Include site configs include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } # Site config: /etc/nginx/sites-available/default server { listen 80; listen [::]:80; server_name _; root /var/www/html; index index.html index.htm; location / { try_files $uri $uri/ =404; } location /api/ { proxy_pass http://localhost:3000; 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; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } - Step 6
Serving Static Content
NGINX excels at serving static files (HTML, CSS, JS, images, videos). Here's how to configure it for optimal static file delivery:
Best practices:
- Enable gzip compression for text files
- Set proper cache headers for browser caching
- Use
try_filesfor clean URLs - Configure proper MIME types
# Create test site sudo mkdir -p /var/www/myapp sudo nano /var/www/myapp/index.html # Add this content: # <html><body><h1>Hello from NGINX!</h1></body></html> # Create site config sudo nano /etc/nginx/sites-available/myapp # Add configuration: server { listen 80; server_name myapp.local; root /var/www/myapp; index index.html index.htm; # Serve static files with caching location / { try_files $uri $uri/ =404; # Browser caching expires 1y; add_header Cache-Control "public, immutable"; } # CSS, JS, images - cache for 1 year location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # API files - no caching location ~* \.(json)$ { expires -1; add_header Cache-Control "no-cache"; } } # Enable site sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ # Test and reload sudo nginx -t sudo systemctl reload nginx # Test (add to /etc/hosts: 127.0.0.1 myapp.local) curl http://myapp.local - Step 7
Reverse Proxy
NGINX as a reverse proxy forwards client requests to backend servers (Node.js, Python, PHP-FPM, etc.). This is one of NGINX's most common uses.
Key proxy directives:
proxy_pass: Backend server addressproxy_set_header: Headers to forward to backendproxy_connect_timeout: Connection timeoutproxy_read_timeout: Response read timeout
# Example: Node.js/Express app on port 3000 # Create site config sudo nano /etc/nginx/sites-available/myapp server { listen 80; server_name myapp.local; # Static files served by NGINX location /static/ { alias /var/www/myapp/public/ expires 1y; add_header Cache-Control "public, immutable"; } # API proxy to Node.js backend location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; # Essential headers proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket support proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; # Buffer sizes (for large responses) proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } # WebSocket specific endpoint location /ws/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400s; } } # Enable and test sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx - Step 8
Load Balancing
NGINX can distribute traffic across multiple backend servers, improving performance and reliability:
Load balancing methods:
round_robin: Default, distributes evenlyleast_conn: Send to server with fewest connectionsip_hash: Consistent hashing by client IPhash: Hash based on any variable
Health checks:
max_fails: Number of failures before marking downfail_timeout: Time to mark server as downbackup: Reserve backup servers
# Define upstream servers upstream backend { # Round-robin (default) server 192.168.1.10:8080; server 192.168.1.11:8080; server 192.168.1.12:8080 backup; } # Or with least connections upstream backend { least_conn; server 192.168.1.10:8080; server 192.168.1.11:8080; server 192.168.1.12:8080; } # Or with IP hashing (session persistence) upstream backend { ip_hash; server 192.168.1.10:8080; server 192.168.1.11:8080; } # Or weighted (for different server capacities) upstream backend { server 192.168.1.10:8080 weight=3; server 192.168.1.11:8080 weight=2; server 192.168.1.12:8080 weight=1; } # With health checks upstream backend { server 192.168.1.10:8080 max_fails=3 fail_timeout=30s; server 192.168.1.11:8080 max_fails=3 fail_timeout=30s; server 192.168.1.12:8080 backup max_fails=3 fail_timeout=30s; } server { listen 80; server_name api.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; proxy_set_header X-Forwarded-Proto $scheme; } # Monitor upstream status (nginx_plus only) location /nginx_status { stub_status on; allow 127.0.0.1; deny all; } } # Test load balancing curl http://api.example.com -I curl http://api.example.com -I curl http://api.example.com -I # Each request should hit a different backend - Step 9
SSL/TLS Configuration
Secure your NGINX server with HTTPS using SSL/TLS certificates. You can use Let's Encrypt for free certificates or purchase from a CA.
SSL best practices:
- Use TLS 1.2 or 1.3 only
- Strong cipher suites
- OCSP stapling for faster certificate validation
- HSTS (HTTP Strict Transport Security)
# Install Certbot for Let's Encrypt sudo apt install certbot python3-certbot-nginx # Obtain certificate sudo certbot --nginx -d example.com -d www.example.com # Or manual SSL config with self-signed cert sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /etc/ssl/private/nginx-selfsigned.key \ -out /etc/ssl/certs/nginx-selfsigned.crt # SSL server configuration server { listen 80; server_name example.com; # Redirect HTTP to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name example.com; # SSL certificates ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL settings 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_timeout 1d; ssl_session_cache shared:SSL: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; # HSTS (enable after confirming SSL works) add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Your content location / { root /var/www/html; try_files $uri $uri/ =404; } } # Auto-renew certificates sudo crontab -e # Add: 0 3 * * * certbot renew --quiet - Step 10
Caching
NGINX can cache backend responses, reducing load on your application servers and improving response times:
Caching features:
proxy_cache_path: Define cache zoneproxy_cache: Enable caching for locationproxy_cache_valid: Set cache duration by statusproxy_cache_key: Define cache keyadd_header X-Cache: Debug cache status
# Define cache zone (in http block) http { proxy_cache_path /var/cache/nginx levels=1:2 \ keys_zone=app_cache:10m \ max_size=1g \ inactive=60m \ use_temp_path=off; server { listen 80; server_name api.example.com; location / { proxy_pass http://backend; # Enable caching proxy_cache app_cache; proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; # Cache key proxy_cache_key $scheme$proxy_host$request_uri; # Cache headers for debugging add_header X-Cache-Status $upstream_cache_status; # Bypass cache for certain requests if ($request_method !~ ^(GET|HEAD)$) { proxy_no_cache 1; } if ($http_authorization) { proxy_no_cache 1; } if ($arg_nocache) { proxy_no_cache 1; } } # Clear cache location location ~ "/api/clear-cache$" { proxy_cache_lock on; proxy_cache_use_stale updating; } } } # Test caching curl -I http://api.example.com/slow-endpoint curl -I http://api.example.com/slow-endpoint # Second request should be faster (X-Cache-Status: HIT) # Clear cache manually sudo rm -rf /var/cache/nginx/* - Step 11
Security Hardening
Secure your NGINX installation with these essential security measures:
Security best practices:
- Hide NGINX version
- Disable unnecessary HTTP methods
- Set security headers
- Limit request sizes
- Implement rate limiting
- Block malicious IPs
# Security configuration server { listen 80; server_name example.com; # Hide NGINX version server_tokens 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; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # Block access to hidden files location ~ /\. { deny all; access_log off; log_not_found off; } # Limit request size client_max_body_size 10M; # Rate limiting limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s; location / { limit_req zone=one burst=20 nodelay; # Your content... } # Block common attacks location ~* "\.(php|asp|aspx|jsp|exe|bat)$" { deny all; } # Allow only specific methods location /api/ { if ($request_method !~ ^(GET|POST|PUT|DELETE)$) { return 405; } proxy_pass http://backend; } } # IP blocking in http block geo $block_ip { default 0; # Add malicious IPs 192.168.100.1 1; 10.0.0.100 1; } map $block_ip $allow_request { 0 1; 1 0; } server { if ($allow_request = 0) { return 403; } # Rest of config... } # Fail2ban integration sudo apt install fail2ban sudo nano /etc/fail2ban/jail.local # Add to jail.local: [nginx-http-auth] enabled = true filter = nginx-http-auth logpath = /var/log/nginx/*error.log maxretry = 3 [nginx-botsearch] enabled = true filter = nginx-botsearch logpath = /var/log/nginx/*access.log maxretry = 2 sudo systemctl restart fail2ban - Step 12
Monitoring & Debugging
Monitor NGINX performance and debug issues with built-in tools and external monitoring:
Built-in monitoring:
stub_status: Basic status endpointnxt_page: More detailed stats (requires nginx-plus or third-party module)- Access/error logs: Debug issues
External tools:
- Prometheus + Grafana
- New Relic
- Datadog
- GoAccess (real-time log analyzer)
# Add stub status to config server { listen 127.0.0.1:8080; server_name localhost; location /nginx_status { stub_status on; allow 127.0.0.1; deny all; } } # Check status curl http://127.0.0.1:8080/nginx_status # Active connections: 12 # accepted: 123456, handled: 123456, requests: 987654 # Reading: 0 Writing: 10 Waiting: 2 # Real-time log tailing tail -f /var/log/nginx/access.log tail -f /var/log/nginx/error.log # Log analysis (install GoAccess) sudo apt install goaccess goaccess /var/log/nginx/access.log -o /var/www/goaccess/index.html --real-time-html # Performance testing with ab (Apache Bench) ab -n 1000 -c 100 http://localhost/ # Performance testing with wrk (more modern) sudo apt install wrk wrk -t4 -c100 -d30s http://localhost/ # Check open files and connections ss -tlnp | grep nginx lsof -i :80 lsof -i :443 # Check worker processes ps aux | grep nginx # View configuration cat /etc/nginx/nginx.conf nginx -T # Show all active config # Reload config test sudo nginx -t # Check error rate sudo grep -c "404" /var/log/nginx/access.log sudo grep -c "500" /var/log/nginx/access.log sudo grep -c "502" /var/log/nginx/access.log - Step 13
Common Use Cases
1. Frontend + Backend Separation:
server { listen 80; server_name example.com; # Frontend (static files) location / { root /var/www/frontend; try_files $uri $uri/ /index.html; } # Backend API location /api/ { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }2. Multiple Applications:
server { listen 80; server_name app1.example.com; root /var/www/app1; } server { listen 80; server_name app2.example.com; root /var/www/app2; }3. Subdomain Routing:
server { listen 80; server_name ~^(?<subdomain>.+)\.example\.com$; root /var/www/sites/$subdomain; }4. CDN Integration:
location / { proxy_pass https://cdn-provider.com$request_uri; proxy_set_header Host $http_host; proxy_set_header Range $http_range; proxy_pass_header Accept-Ranges; proxy_pass_header Content-Range; }5. API Versioning:
location /api/v1/ { rewrite ^/api/v1/(.*)$ /api/v2/$1 break; proxy_pass http://backend; } location /api/v2/ { proxy_pass http://backend; }6. Maintenance Mode:
set $maintenance 0; if ($request_uri ~ "^/$" ) { set $maintenance 1; } location / { if ($maintenance = 1) { return 503; } proxy_pass http://backend; } location = /503.html { internal; root /var/www/maintenance; }7. Hotlink Protection:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { valid_referers none blocked example.com www.example.com; if ($invalid_referer) { return 403; } }8. Custom Error Pages:
error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /404.html { root /var/www/errors; internal; } location = /50x.html { root /var/www/errors; internal; }# Full example: Production-ready config for Node.js app # /etc/nginx/sites-available/production server { listen 443 ssl http2; server_name example.com www.example.com; # SSL ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; # 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; # Static files location /static/ { alias /app/public/ expires 1y; add_header Cache-Control "public, immutable"; } # API proxy location /api/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Health check location /health { access_log off; return 200 "OK\n"; add_header Content-Type text/plain; } # Deny access to hidden files location ~ /\. { deny all; access_log off; log_not_found off; } # Error pages error_page 500 502 503 504 /50x.html; location = /50x.html { root /var/www/errors; } } # HTTP redirect server { listen 80; server_name example.com www.example.com; return 301 https://$server_name$request_uri; } - Step 14
Resources & Next Steps
Documentation:
- NGINX Official Docs
- NGINX HTTP Server Module Directive Index
- NGINX Configuration Guides
- NGINX Cookbook
Learning Resources:
Community:
Tools:
- NGINX Status Monitor
- Squidex - NGINX log analyzer
- GoAccess - Real-time log analyzer
Next guides:
- NGINX Plus for advanced features
- NGINX App Protect WAF
- NGINX Ingress Controller for Kubernetes
- Advanced NGINX caching strategies
GitHub: https://github.com/nginx/nginx Official site: https://nginx.org Documentation: https://nginx.org/en/docs/ Commercial: https://www.nginx.com
Feature requests
Sign in to suggest features or vote on existing ones.
No feature requests yet.
Discussion
Sign in to join the discussion.
No comments yet.