SynfraCore
Synfracore
Start Learning
Navigation

Academies

Platform

RoadmapsLabsCertificationsInterviewPYQsAI AssistantCareer
Start Learning Free Learning Roadmaps

Nginx β€” Overview

What it is, why it matters, architecture and key concepts

πŸ“„
Last updated Aug 2026
Expert Content

Nginx β€” Web Server, Reverse Proxy & Load Balancer

Before you start: basic Linux/CLI comfort and a rough idea of what HTTP/HTTPS and DNS do are assumed. No prior web-server administration experience is needed.

Nginx (pronounced "engine-x") is the most widely deployed web server in the world. It powers over 34% of all websites and is the default reverse proxy in Kubernetes ingress, Docker environments, and cloud architectures.

Analogy β€” Think of Nginx like a hotel front desk, not a hotel room. Guests never walk directly to a specific room on their own β€” they go to the front desk, which checks them in, decides which room they actually get, and can even say "that room's full, here's another one nearby" (load balancing across backends). The front desk also handles things no individual room should have to (checking ID at check-in, holding packages, security) β€” that's SSL termination and rate limiting, handled once at the front door instead of by every backend server individually.

What Nginx Does

Internet β†’ Nginx β†’ Application Servers
            β”‚
            β”œβ”€β”€ Reverse proxy (forward requests to backend)
            β”œβ”€β”€ Load balancer (distribute across multiple backends)
            β”œβ”€β”€ SSL/TLS termination (handle HTTPS, pass HTTP internally)
            β”œβ”€β”€ Static file serving (images, CSS, JS β€” extremely fast)
            β”œβ”€β”€ Rate limiting (protect APIs from abuse)
            β”œβ”€β”€ Caching (cache backend responses)
            └── HTTP/2 and gRPC support (gRPC β€” a fast RPC framework often used for service-to-service calls)
Reverse Proxy
Forwards requests to backend servers, hiding them from clients
Load Balancer
Distributes requests across multiple backends
SSL/TLS Termination
Handles HTTPS at the edge, passes plain HTTP internally
Static File Serving
Images, CSS, JS β€” extremely fast, no backend involved
Rate Limiting
Protects APIs from abuse β€” caps requests per client
Caching
Caches backend responses to reduce repeat load

Installation

bash
# Ubuntu/Debian
sudo apt install nginx
sudo systemctl enable --now nginx

# Check status and version
sudo systemctl status nginx
nginx -v          # nginx/1.24.0
nginx -t          # Test config syntax

# Key file locations
# /etc/nginx/nginx.conf          Main config
# /etc/nginx/sites-available/    Virtual host configs
# /etc/nginx/sites-enabled/      Symlinks to active configs
# /var/log/nginx/access.log      Access log
# /var/log/nginx/error.log       Error log
# /var/www/html                  Default web root

Core Configuration Concepts

nginx
# nginx.conf structure
events {
    worker_connections 1024;   # Max connections per worker
}

http {
    # Global settings apply to all servers
    gzip on;
    gzip_types text/plain application/json application/javascript text/css;

    # Include all virtual host configs
    include /etc/nginx/sites-enabled/*;
}

Virtual Hosts (Server Blocks)

nginx
# /etc/nginx/sites-available/myapp.conf

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

    # Redirect all HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name myapp.com www.myapp.com;

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

    # SSL hardening
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy no-referrer-when-downgrade;

    # Logging
    access_log /var/log/nginx/myapp_access.log;
    error_log  /var/log/nginx/myapp_error.log;

    # Serve static files directly (fast!)
    location /static/ {
        root /var/www/myapp;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Proxy to Node.js/Python/whatever backend
    location / {
        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_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_cache_bypass $http_upgrade;
        proxy_read_timeout 60s;
        proxy_connect_timeout 10s;
    }
}

Load Balancing

nginx
http {
    # Define upstream group
    upstream api_backend {
        # Load balancing algorithms:
        # (default = round-robin)
        # least_conn;          # Least connections
        # ip_hash;             # Sticky sessions by client IP
        # hash $request_uri;   # Hash-based (consistent)

        server 10.0.0.1:3000 weight=3;   # Gets 3x traffic
        server 10.0.0.2:3000 weight=1;
        server 10.0.0.3:3000 backup;     # Only used when others fail

        # Health checks
        keepalive 32;                     # Persistent connections
    }

    upstream api_backend_ha {
        least_conn;
        server 10.0.1.1:8080 max_fails=3 fail_timeout=30s;
        server 10.0.1.2:8080 max_fails=3 fail_timeout=30s;
        server 10.0.1.3:8080 max_fails=3 fail_timeout=30s;
    }

    server {
        location /api/ {
            proxy_pass http://api_backend;
        }
    }
}

Rate Limiting

nginx
http {
    # Define rate limit zones
    # $binary_remote_addr = client IP (4 bytes, efficient)
    # zone=api:10m = 10MB shared memory zone named "api"
    # rate=10r/s = max 10 requests per second per IP
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=2r/m;

    server {
        # Apply rate limit to API
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            # burst=20: allow burst of 20 requests
            # nodelay: serve burst immediately, don't queue
            proxy_pass http://api_backend;
        }

        # Stricter limit for login endpoint
        location /auth/login {
            limit_req zone=login burst=5 nodelay;
            limit_req_status 429;
            proxy_pass http://api_backend;
        }
    }
}

Caching

nginx
http {
    # Define cache zone
    proxy_cache_path /var/cache/nginx 
        levels=1:2 
        keys_zone=api_cache:10m 
        max_size=1g 
        inactive=60m 
        use_temp_path=off;

    server {
        location /api/public/ {
            proxy_cache api_cache;
            proxy_cache_valid 200 5m;       # Cache 200 responses for 5 min
            proxy_cache_valid 404 1m;       # Cache 404s for 1 min
            proxy_cache_use_stale error timeout updating;
            proxy_cache_background_update on;
            proxy_cache_lock on;

            # Cache key includes URI and query string
            proxy_cache_key "$scheme$request_method$host$request_uri";

            add_header X-Cache-Status $upstream_cache_status;
            proxy_pass http://api_backend;
        }
    }
}

Nginx as Kubernetes Ingress

yaml
# ingress-nginx controller handles all K8s Ingress resources
# Install via Helm:
# helm install ingress-nginx ingress-nginx/ingress-nginx

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "10"
    # Redirect HTTP to HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Custom timeout
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    # Enable CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "https://myapp.com"
    # Basic auth
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.myapp.com
    secretName: myapp-tls
  rules:
  - host: api.myapp.com
    http:
      paths:
      - path: /v1/
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
      - path: /auth/
        pathType: Prefix
        backend:
          service:
            name: auth-service
            port:
              number: 80

Performance Tuning

nginx
# nginx.conf - production tuning
worker_processes auto;          # One worker per CPU core
worker_rlimit_nofile 65535;     # Max open files per worker

events {
    worker_connections 65535;   # Max connections per worker
    use epoll;                  # Linux's efficient I/O event-notification mechanism (fastest option)
    multi_accept on;            # Accept multiple connections at once
}

http {
    # Disable nginx version in error pages (security)
    server_tokens off;

    # Sendfile β€” kernel handles file transfer (much faster for static files)
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # Timeouts
    keepalive_timeout 65;
    keepalive_requests 1000;
    client_header_timeout 10s;
    client_body_timeout 10s;
    send_timeout 10s;

    # Buffer sizes
    client_body_buffer_size 128k;
    client_max_body_size 50m;   # Max upload size
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;          # 1-9, 6 is good balance
    gzip_min_length 256;
    gzip_types
        text/plain text/css text/xml text/javascript
        application/json application/javascript application/xml
        image/svg+xml;
}

Useful Commands

bash
# Test config syntax (always before reload!)
sudo nginx -t

# Reload without dropping connections
sudo nginx -s reload

# Full restart
sudo systemctl restart nginx

# View access log in real-time
sudo tail -f /var/log/nginx/access.log

# View error log
sudo tail -f /var/log/nginx/error.log

# Check which config files are loaded
sudo nginx -T | head -50

# Enable a site
sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo nginx -s reload

# Disable a site
sudo rm /etc/nginx/sites-enabled/myapp.conf
sudo nginx -s reload

Interview Questions

What is the difference between a reverse proxy and a load balancer?

A reverse proxy sits in front of one or more servers and forwards client requests to them, hiding the backend from clients. A load balancer distributes incoming requests across multiple backend servers for scalability and high availability. Nginx does both β€” as a reverse proxy it handles SSL termination, caching, and header manipulation; as a load balancer it distributes traffic using algorithms like round-robin, least_conn, or ip_hash. In practice, the distinction is blurry because every production load balancer is also a reverse proxy.

What is the difference between proxy_pass http://backend/ and proxy_pass http://backend?

The trailing slash matters. With location /api/ and proxy_pass http://backend/, nginx strips /api/ from the URI β€” /api/users becomes /users on the backend. Without the slash, proxy_pass http://backend, nginx passes the full URI β€” /api/users remains /api/users. Most APIs want the trailing slash version to strip the path prefix.

Share:
Join our Community
Daily tips, job alerts, interview help β€” join engineers learning together
β†’
Up Next
βœ…
Nginx β€” Prerequisites
What to know or set up before starting
Also Worth Exploring
← Back to all Nginx modules
Prerequisites β†’