Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

1.1 The Web Server Landscape: Where NGINX Fits

Concept-heavy, hands-light. The hands arrive in 1.2 — this lesson builds the vocabulary.

Levels: Beginner ~25 min read

Basic

What you will learn

  1. What a web server actually is — the three jobs hiding behind that name.

  2. The C10K problem, and why it spawned NGINX in 2002.

  3. Process-per-connection vs. event-driven architectures — with diagrams.

  4. The modern roles NGINX plays beyond "serving files".

  5. Open source NGINX vs. NGINX Plus — what money actually buys.


1. Three jobs called "web server"

When someone says "web server", they may mean any of three different things, and NGINX is all three at once. Keeping these separate in your head will make every later configuration decision easier:

Job

Name

Question it answers

1. File serving

Web server

"Can you read index.html off disk and send it to me?"

2. Protocol termination

HTTP server

"Can you parse my HTTP/1.1, later my TLS, maybe my HTTP/3?"

3. Routing & proxying

Reverse proxy / gateway

"Where should this request actually go, and what should happen to it on the way?"

A pure file server (like Python's http.server) does only job 1. NGINX was designed, from its first line of code, to be excellent at jobs 2 and 3 — file serving came almost as a side effect of being good at moving bytes.

2. 2002: the C10K problem

In the late 1990s the dominant web server, Apache, used a simple model: one process (or thread) per connection. A web page with 30 images and a keep-alive browser meant dozens of live processes per visitor. At 10,000 simultaneous connections — "the C10K problem" — the server spent its CPU not on HTTP, but on scheduling itself: context switches, lock contention, and memory for thousands of mostly-idle stacks.

Igor Sysoev's answer, first released in 2004 and written while he ran one of Russia's largest sites (Rambler), was radical simplicity:

Don't spawn anything per connection. Spawn a fixed number of workers up front, and let each one juggle thousands of connections inside a loop.

That is NGINX: N processes (one per CPU core), each running an event loop.

3. The two architectures, side by side

Process-per-connection (classic Apache prefork)

text
     client connections:  ●───●───●───●───●───●
                           │   │   │   │   │   │
                        [proc][proc][proc][proc][proc][proc]   ← 1 process EACH
                           │   │   │   │   │   │              (blocked, reading
                           ▼   ▼   ▼   ▼   ▼   ▼               or just waiting)
                              kernel socket buffers

Each connection pins one process. If the client is slow (or idle on keep-alive), the process blocks — it occupies memory and a scheduler slot while doing nothing. 10,000 connections ≈ 10,000 processes. The kernel burns most of its time switching between them.

Event-driven (NGINX)

text
     client connections:  ●───●───●───●───●───●───●───●───●───●
                           │   │   │   │   │   │   │   │   │   │
                        ╲  │ ╲ │ ╲ │ ╲ │ ╲ │   ... all multiplexed ...
                          ▼   ▼   ▼
                        ┌─────────────┐        ┌─────────────┐
                        │ worker (core0)│       │ worker (core1)│   ← 2 fixed workers
                        │ event loop:  │        │ event loop:  │      (2-core machine)
                        │  epoll_wait()│        │  epoll_wait()│
                        └─────────────┘        └─────────────┘

Each worker calls the kernel's epoll_wait() and asks one question: "which of my thousands of sockets have something for me right now?" The kernel returns a short list of ready sockets; the worker services exactly those, in a non-blocking way, and loops. Slow clients cost nothing — a socket that has nothing to do simply never appears in the ready list.

The cost of the trade

Event-driven is not free, and an honest engineer knows the price:

  • Blocking is banned. One blocking syscall (disk I/O, DNS) would freeze the whole worker's loop. NGINX therefore wraps everything — file I/O, upstream connects, sends — in non-blocking state machines, and offloads what it must (e.g. reading large request bodies to disk) carefully.

  • CPU-bound work doesn't mix. The loop is for I/O. Heavy per-request computation (rendering, ML) does not belong in NGINX — it belongs behind NGINX. This is why NGINX is a gateway and your app is the application.

  • Shared state needs discipline. Workers don’t share per-request memory; anything cross-worker (rate-limit counters, caches) lives in explicit shared-memory zones — a concept you will configure in Lesson 7.2 (limit_req_zone) and Lesson 8.2 (proxy_cache_path’s keys_zone).

4. Where NGINX sits today

The event-driven core made NGINX the natural choice whenever something needs to sit in front of other things. Its modern roles, in rough order of how often you'll meet them:

Role

What it does

Covered in

Reverse proxy

Accepts client traffic, forwards to backend apps

Module 4

Load balancer

Spreads traffic across a pool of backends

Module 5

TLS termination point

Decrypts at the edge, speaks plain HTTP inside

Module 6

API gateway

Routing, rate limits, auth, traffic shaping

Modules 4, 7, 11

CDN edge / cache

Serves repeated content from memory/disk

Module 8

Kubernetes ingress

The front door of a cluster

Module 14

L4 TCP/UDP proxy

Routes raw TCP/UDP (databases, media, MQTT)

Module 10

Observable control point

Logging, metrics, tracing of all traffic

Module 12

One binary, one config language, all of these roles. That economy of concepts is why NGINX knowledge compounds: everything you learn in Module 4 about proxying is reused by Modules 5–14.

5. Open source vs. NGINX Plus

Everything in this series applies to open-source NGINX (the nginx.org binary, BSD-2-Clause-style license, currently maintained by F5 after acquiring NGINX Inc. in 2019). A commercial sibling, NGINX Plus, adds proprietary features on top of the same core. Know what's behind the paywall so you can decide consciously:

Capability

Open source

NGINX Plus

Core proxying, load balancing, TLS, caching

✅

✅

Active health checks (probes backends in the background)

❌ (passive only — Module 5)

✅

Dynamic upstream API (add/remove backends without reload)

❌

✅

Session persistence (sticky learn/cookie)

limited ( hash, ip_hash)

✅ advanced

Built-in dashboard & extended status API

❌ (Module 12 builds one)

✅

JWT / SSO auth validation built in

❌ (njs can do it — Module 15)

✅

Commercial support & SLAs

❌

✅

6. Verify your own setup

Everything from Lesson 1.2 onward was executed against a real NGINX before it reached this page. Where the stable and mainline branches behave differently, this series shows both — 1.30.5 and 1.31.6 at the time of writing. A plain curl already reveals the server introducing itself:

bash
$ nginx -v
nginx version: nginx/1.30.5
$ curl -si http://127.0.0.1/ | head -2
HTTP/1.1 200 OK
Server: nginx/1.30.5

That Server: header is NGINX doing job 2 (protocol) and job 1 (file) before you have written a single line of configuration. In Module 2 you’ll learn to hide or reshape that header — and when you shouldn’t.

Try it yourself

  1. Check the version and build you run: nginx -v for the version, nginx -V for the compile-time story (Lesson 1.3 dissects it).

  2. Read your own server’s greeting: curl -si http://127.0.0.1/ | head -2 — and find out which branch your distro ships in Lesson 1.2.

  3. Reload and watch the Server: header survive — the machinery behind that is Module 1’s running theme.

Q&A

Q1. A browser holds 4 keep-alive connections open but is idle for a minute. What is each NGINX worker doing for it during that minute?

Answer

Nothing — and that's the design. Idle sockets are not in the worker's epoll_wait() ready list, so they cost no CPU. The connection occupies a worker_connections slot and a little kernel memory, nothing more. An Apache prefork server, by contrast, would have 4 processes blocked in read() for that minute.

Q2. Your product team wants to add image thumbnail generation (CPU-heavy librsvg work) to the edge. Good NGINX design or bad?

Answer

Bad inside NGINX — the event loop must never host CPU-bound work (see §3's costs). Good NGINX design: NGINX routes /thumb/* via proxy_pass to a small thumbnail service (Module 4), optionally caches the results at the edge (Module 8), and rate-limits abuse (Module 7). Gateway + cache + app, not web-server-as-render-farm.

Q3. Name the three "jobs" from §1 for this request: GET /api/users that arrives encrypted, is decrypted, matched to a route, and forwarded to a Node.js backend.

Answer

Job 2 (protocol termination: TLS + HTTP parsing), job 3 (routing/proxying: location match → proxy_pass), and — strictly — job 1 barely appears because no disk file is served. This is the "API gateway" role from §4.

Q4. Your CTO asks why you listed NGINX Plus in a procurement discussion if "open source is the same speed." What's the honest one-liner?

Answer

"Plus buys operations — active health checks, dynamic upstream API, support — not speed; we can start open source and only pay if we hit those walls."

Takeaways

  • NGINX = a small fixed set of worker processes, each running an event loop ( epoll) over thousands of non-blocking connections.

  • It was born from the C10K problem; its answer was architectural, not tuning: never do per-connection work for idle connections.

  • The blocking ban explains NGINX's design everywhere: state machines, shared-memory zones, and the "put heavy work behind the proxy" philosophy.

  • NGINX's modern center of gravity is the edge: proxy, load balancer, TLS terminator, gateway, cache, ingress.

  • Open source vs. Plus differ in operational features, not performance.

Was this page helpful?

Mark this learning resource complete to track your learning progress.