00. nginx — ELI5¶
nginx is a hyper-efficient receptionist who never sleeps. One desk, one phone, one queue, but ten thousand callers at a time. He answers the phone, decides which back office handles the call, hands off, and never blocks on a single conversation.
Picture a big building with many back offices — one for static documents, one for the application server, one for the API. Visitors arrive at the front desk. They want different things. They speak different protocols (HTTP, HTTPS, WebSocket). Some are fast; some are slow.
A traditional receptionist talks to one visitor at a time, fully, before turning to the next. If a visitor is slow ("uhh... let me find my ID..."), the queue grows. The building loses business.
nginx does it differently. He listens to many visitors at once. When a visitor pauses to think, nginx switches attention to another visitor who has spoken. When the slow one is ready, nginx comes back. One worker, hundreds of conversations interleaved.
He is also a router. The slip says "GET /static/logo.png" — nginx fetches it from a nearby drawer (the disk) and hands it over without bothering the back office. "GET /api/orders/" — nginx hands the slip to the API office (gunicorn or another app server) and waits for the response. "POST /upload/" with a 200 MB file — nginx buffers the upload locally, so the slow upload doesn't tie up the back office's worker.
That is nginx. Receptionist. Router. Buffer. Static-file server. TLS terminator. Compressor. Cacher. All in one process, all event-driven, all configurable.
The recurring vocabulary¶
| Name | What it is |
|---|---|
| worker process | one nginx OS process that handles connections via an event loop |
| event loop | the mechanism (epoll/kqueue) that lets one worker serve many connections |
| listener / server block | a config block defining what port + hostname this nginx instance answers |
| location block | a config block matching a URL path pattern within a server |
| upstream | the back-end pool nginx forwards requests to |
| proxy_pass | the directive that sends a request to an upstream |
| TLS termination | nginx decrypts HTTPS; talks to the upstream over HTTP |
| static files | files served directly from disk without an app server |
| buffering | nginx reads slow client/upstream into memory or disk before passing on |
| reverse proxy | the role of standing between clients and an upstream |
| forward proxy | the role of standing between clients and the open internet |
The picture¶
internet
│ (lots of slow clients, mobile, dropped connections)
▼
┌─────────────────────┐
│ nginx (master) │ one process; configures listeners
└─────────┬───────────┘
│ forks
┌─────────┼─────────┬─────────────────┐
▼ ▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐ ┌────────┐
│ wkr 1 │ │ wkr 2 │ │ wkr 3 │ ... │ wkr N │ one worker per CPU
└───┬───┘ └───┬───┘ └───┬───┘ └───┬────┘
│ │ │ │
│ each worker runs an event loop; │
│ serves thousands of connections │
└─────────┴─────────┴─────────────────┘
│
├──► /static/* → /var/www/static/ (disk, no app server)
│
├──► /api/* → upstream: gunicorn at 127.0.0.1:8000
│
├──► /ws/* → upstream: daphne at 127.0.0.1:9000 (WebSocket)
│
└──► / → upstream: gunicorn at 127.0.0.1:8000
One master process spawns N worker processes (one per CPU, typically). Each worker handles thousands of concurrent connections by using the OS's event-notification mechanism. There is no thread per request, no process per request — just one worker doing many things at once.
Why nginx beats a thread-per-request server at scale¶
The traditional model:
1000 incoming requests → 1000 threads (or processes).
Each thread blocks on its own network I/O.
1000 × thread stack memory = 8 MB × 1000 = 8 GB.
1000 × context switches per second.
nginx's model:
1000 incoming requests → handled by 4 workers (one per CPU).
Each worker has one event loop monitoring 250 connections.
When a connection has data, the worker processes it; when waiting, the worker handles another.
Memory: ~ 10 MB per worker × 4 = 40 MB.
Context switches: negligible.
The difference is felt at 1000+ concurrent connections (the C10K problem). nginx solved this in 2002; it remains the reference for evented HTTP servers.
What nginx does well and what it doesn't¶
nginx is excellent at:
- Serving static files (faster than any application server).
- Reverse-proxying to application servers (gunicorn, uvicorn, Node, Go services).
- Terminating TLS.
- Buffering slow clients (the application server never sees the slow upload).
- Gzip / Brotli compression.
- Caching (proxy_cache).
- Load balancing across upstream pools.
- Rate limiting and connection limiting.
nginx is not for:
- Application logic (no scripting beyond OpenResty's Lua).
- Heavy templating (use the application).
- Complex business rules (use the application).
- Stateful workflows (use the application).
The pattern: nginx in front, application server behind, every responsibility allocated to the layer that handles it best.
What this module covers¶
- 01-event-loop-workers-internals.md — How the event loop, worker processes, and listener sockets actually work; the model that produces nginx's scale.
- 02-configs-locations-day-to-day.md — The configuration surface: server, location, proxy_pass, headers, the patterns developers write daily.
- 03-tls-caching-prod-gotchas.md — TLS, caching, rate limiting, and the production gotchas every team eventually hits.
Bridge. Before writing nginx configs, we see what makes one nginx worker handle thousands of connections. Chapter 01 opens the event loop. → 01-event-loop-workers-internals.md