nginx
Posts
-
Digging into nginx's Source 1: How I Read It with AI, and the Map
The first post of a series on reading nginx's source, starting with pictures and a 3-minute hands-on run that show what nginx is, how it works and what it is for. It then covers which version we read (the 1.31.9 mainline development version) and how it differs from stable 1.30.x, how to get the same source and build it in Docker with auto/configure and make, a folder map of the 260,000-line source, the main path one request takes from accept to sendfile — checked with gdb and strace — and how to read with AI while verifying its notes against the source.
-
Digging into nginx's Source 2: The Big Picture — Master and Workers, Modules, the Cycle and Config Contexts
The map for the whole series, written for intermediate programmers opening the nginx source for the first time. Where the single master and the workers each spend their time waiting, how every feature plugs in as one ngx_module_t and why module types are the ASCII letters "CORE" and "HTTP", what ngx_cycle_t and conf_ctx hold for a running nginx, how the main, events, http, server and location contexts of nginx.conf are checked with bit flags, and how one worker's event loop juggles hundreds of connections — all checked against the real source with ps and gdb examples.
-
Digging into nginx's Source 3: Memory Pools and Core Data Structures — ngx_pool_t, ngx_str_t, ngx_buf_t and ngx_chain_t
You will hardly find free() in nginx's C code either. Memory that shares a lifetime — one request, one connection — goes into a pool and is thrown away in one go. This post checks, against the real source, debug logs and gdb, how ngx_pool_t chains its blocks and uses the failed counter, the separate large list for big allocations, cleanups, the non-NUL-terminated ngx_str_t, arrays, lists, queues and hashes, and the ngx_buf_t and ngx_chain_t that carry responses.
-
Digging into nginx's Source 4: Startup and Reading the Config — From main() to ngx_init_cycle
When you start nginx, main() reads the command line, and ngx_init_cycle() reads nginx.conf and builds one complete set of configuration called a cycle — only then does the master process start running. We follow how one parser function handles three cases (a file, a block, and the -g command-line string), how ngx_conf_read_token() splits text into words one character at a time, where the error messages come from, and how reload calls the same ngx_init_cycle() again and inherits listening sockets and shared memory from the old cycle — using the nginx 1.31.9 source and gdb and strace examples.
-
Digging into nginx's Source 5: Modules and Directives — How One Line of nginx.conf Becomes a Module's Config Struct
An nginx module is a single ngx_module_t struct, and a directive is one row in the ngx_command_t table that struct points to. We follow the module array configure generates, dynamic modules read with load_module, ngx_conf_handler() finding a function by directive name, ngx_http_block() creating per-module config structs for every http, server and location, and the merge that finishes once, at config time. We build a dynamic module with four directives to watch the callback order, the merged values and the error messages.
-
Digging into nginx's Source 6: Master and Workers — Processes, Signals and Zero-Downtime Reload
We follow src/os/unix/ngx_process_cycle.c to see how one nginx -s reload wakes the master's sigsuspend() loop, starts new workers with the new config, and lets the old workers finish what they were doing before they leave. Why the signal handler only sets a flag, the socketpair channel between master and workers, the respawn, just_spawn and exiting bits in the ngx_processes[] table, how a worker killed with kill -9 comes back, and the USR2 binary upgrade — all checked with strace, debug logs and ps.
-
Digging into nginx's Source 7: The Event Loop — epoll, Timers, Posted Events and accept
We follow one turn of ngx_process_events_and_timers(), the loop that lets a single-threaded worker juggle thousands of connections. How epoll is plugged in through a function-pointer table (ngx_event_actions), the connection array pre-built to worker_connections slots and its free list, EPOLLET and the instance bit that filters stale events, how the red-black timer tree becomes epoll_wait's timeout, the posted queues, and which of accept_mutex (off by default), EPOLLEXCLUSIVE and reuseport decides who accepts a new connection — checked with strace, gdb and the debug log.
-
Digging into nginx's Source 8: Reading an HTTP Request — From a Connection to ngx_http_request_t
We follow, through the source, how one accepted socket becomes an ngx_http_request_t inside nginx — why the request struct is not created until data arrives, the switch-based state machines that parse the request line and headers, where client_header_buffer_size and large_client_header_buffers are used, the order in which the Host header picks a server block, and keep-alive and pipelining, all checked with debug logs, gdb and nc.
-
Digging into nginx's Source 9: The 11 Phases and Finding the location — In What Order a Request Is Handled
Once its headers are read, a request walks through 11 phases inside nginx. We check in the source how the handlers that modules register are flattened at config time into a single ph[] array, and how ngx_http_core_run_phases() drives that array through checker functions. We also see how location blocks become a prefix tree and are chosen in the order =, ^~, regex and 1.31.x's predicate locations, and which slot of the array rewrite last/break, internal redirects and named locations go back to — with debug logs and gdb.
-
Digging into nginx's Source 10: Filters and Static Files — How a Response Flows Out
We follow one static file from the handler to the socket in the nginx 1.31.9 source. The static handler that never reads the file and only records "from this byte to that byte of the file" in an ngx_buf_t, the header and body filter chains glued together with two function pointers and the ngx_modules.c order that decides them, the copy filter and ngx_output_chain that decide whether to read the file into memory, and the write filter that holds data back and then sends it with writev + sendfile — all checked with gdb, strace and the debug log.
-
Digging into nginx's Source 11: Upstream and Proxy — One Request Through the Reverse Proxy
We follow, in the source, how one proxy_pass line installs a content handler on a location, how the proxy module fills the callbacks of ngx_http_upstream_t, and how upstream then takes care of the non-blocking connect, sending the request, reading the response header and streaming the body. We check the request the backend actually receives (HTTP/1.1 by default in this version, and no Connection header), where 502 and 504 are decided, proxy_next_upstream, and proxy_buffering on/off with its temporary file — using debug logs, gdb and a tiny backend.
-
Digging into nginx's Source 12: Load Balancing, Upstream Keepalive and the Stream Module — and Wrapping Up the Series
When an upstream {} block lists several servers, how does nginx pick one for each request? We trace the load-balancing frame built from three function pointers (peer.init, get, free), the smooth weighted round robin that spreads weights 5:1:1 as a a b a c a a, max_fails and fail_timeout, the keepalive module that wraps get/free to park connections in a cache (on by default in this version, even if you never write it), and the stream module that puts the same foundation under plain TCP — checked against the source and Docker examples — and then wrap up the series.