Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Web Servers — Nginx, Apache, Caddy

Nginx

Architecture

Nginx uses an event-driven, asynchronous architecture:

Master Process (root)
├── Worker Process 1 (epoll/kqueue event loop)
├── Worker Process 2
├── Worker Process 3
└── Worker Process 4
  • Master: Reads config, binds ports, manages workers
  • Workers: Handle connections via event loop (non-blocking)
  • Each worker can handle thousands of concurrent connections
  • No thread-per-connection model (unlike Apache prefork)

Configuration

# /etc/nginx/nginx.conf
worker_processes auto;
events {
    worker_connections 1024;
}

http {
    upstream backend {
        least_conn;
        server 10.0.0.1:8080 weight=3;
        server 10.0.0.2:8080;
        server 10.0.0.3:8080 backup;
    }

    server {
        listen 80;
        server_name example.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;          # nginx >= 1.25.1: use `http2 on;` instead of `listen ... http2`
        http2 on;
        server_name example.com;

        ssl_certificate /etc/ssl/cert.pem;
        ssl_certificate_key /etc/ssl/key.pem;

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

        location /static/ {
            alias /var/www/static/;
            expires 30d;
            add_header Cache-Control "public, immutable";
        }

        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}

Load Balancing Algorithms

AlgorithmDirectiveBehavior
Round Robin(default)Distributes requests evenly
Least Connectionsleast_connSends to server with fewest active connections
IP Haship_hashSame client IP always goes to same server
Weightedweight=NProportional distribution
Randomrandom two least_connRandom with two choices

Reverse Proxy

location /api/ {
    proxy_pass http://backend;  # no trailing slash → request URI passed unchanged (/api/users → /api/users)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_connect_timeout 5s;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
}

Caching

http {
    proxy_cache_path /var/cache/nginx levels=1:2 
                     keys_zone=my_cache:10m max_size=10g;
    
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 1h;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Rate Limiting

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_conn_zone $binary_remote_addr zone=conn:10m;
    
    location /api/ {
        limit_req zone=api burst=20 nodelay;
        limit_conn conn 10;
    }
}

Apache

Architecture (MPM Modules)

MPMModelUse Case
preforkProcess per connectionLegacy, PHP mod_php
workerThreads per processBetter memory usage
eventAsync keep-aliveBest performance, closest to Nginx

Key Features

# Virtual Host
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    
    # Reverse Proxy
    ProxyPass /api/ http://localhost:8080/
    ProxyPassReverse /api/ http://localhost:8080/
    
    # URL Rewriting
    RewriteEngine On
    RewriteRule ^/old/(.*)$ /new/$1 [R=301,L]
    
    # Caching
    CacheEnable disk /
    CacheDefaultExpire 3600
</VirtualHost>

Nginx vs Apache

AspectNginxApache
ArchitectureEvent-drivenProcess/thread-based
Static filesExcellentGood
Dynamic contentProxy to app servermod_php, mod_python
ConfigurationCentralized.htaccess per directory
Memory usageLowHigher (prefork)
Concurrent connections10K+ per workerLimited by processes/threads
Load balancingBuilt-inmod_proxy_balancer

Interview Questions

Q: Why is Nginx faster than Apache for static files? A: Nginx uses event-driven I/O (epoll/kqueue) — a single worker handles thousands of connections without blocking. Apache prefork creates a process per connection, consuming more memory and CPU for context switching. Apache’s event MPM narrows the gap.

Q: What is a reverse proxy and why use it? A: A server that forwards client requests to backend servers. Benefits: (1) SSL termination at proxy, (2) load balancing, (3) caching static content, (4) rate limiting, (5) hiding backend topology, (6) compression.

Q: How does Nginx handle 10,000 concurrent connections with 4 workers? A: Each worker runs a non-blocking event loop using epoll (Linux) or kqueue (BSD). Connections are event-driven — a worker doesn’t block waiting for I/O. A single worker can handle thousands of idle/active connections because only active connections consume CPU.

References