nginx starters, one directive per page
Twenty-seven steps for developers who've shipped sites but never owned the nginx layer: the master and its workers, the nginx.conf tree of directives and contexts, nginx -t and graceful reloads, signals, worker_processes and worker_connections, include, listen, server_name and the Host header, root, the location search order, try_files, mime.types, sendfile, gzip, keep-alive, expires, error_page, proxy_pass and its trailing slash, proxy_set_header, retries, upstream groups, the access log's time fields, error_log levels, client_max_body_size, and TLS with listen 443 ssl and SNI, one directive per page.
A diagram, the classic trap, and one thing to go try this week. That's a page.
nginx starters, one directive per page
Twenty-seven steps for developers who've shipped sites but never owned the nginx layer: the master and its workers, the nginx.conf tree of directives and contexts, nginx -t and graceful reloads, signals, worker_processes and worker_connections, include, listen, server_name and the Host header, root, the location search order, try_files, mime.types, sendfile, gzip, keep-alive, expires, error_page, proxy_pass and its trailing slash, proxy_set_header, retries, upstream groups, the access log's time fields, error_log levels, client_max_body_size, and TLS with listen 443 ssl and SNI, one directive per page.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
nginx behaviour checked against the official nginx documentation (nginx.org/en/docs, mainline), fetched and read during this build: Beginner's Guide; Controlling nginx; Command-line parameters; Core functionality (worker_processes, worker_connections, include, error_log); Events; How nginx processes a request; Configuring HTTPS servers; and the http core, proxy, upstream, gzip, log and headers module references. Concepts and directives are named as the documentation names them. Teaching conventions (one directive a day) are named as conventions. This book quotes no verbatim passages and is an independent guide not affiliated with or endorsed by F5, Inc. NGINX is a trademark of F5, Inc.
General information only. Not professional advice; verify against your own nginx version and the current documentation.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
The model
What nginx is: one master reading config, workers doing the serving, the directive-and-context tree of nginx.conf, test-then-reload, the signals, and the worker sizing knobs.
- 01One master, many workers
- 02nginx.conf, the tree
- 03nginx -t, then reload
nginx has one master process and several worker processes. The main purpose of the master process is to read and evaluate configuration, and maintain worker processes. Worker processes do actual processing of requests. nginx employs an event-based model and OS-dependent mechanisms to efficiently distribute requests among worker processes. Per the nginx official Beginner's Guide.
One master, many workers
Let's say someone says "the site's behind nginx" and you nod. Worth knowing what's actually running.
There's one master process. It reads and evaluates the configuration and maintains worker processes. The workers do the actual request processing, with an event-based model, one worker handling many connections at once. You don't spawn workers per request. You edit nginx.conf, the master notices, workers get the new config.
The master reads. The workers serve.
Run ps -ax | grep nginx on any nginx box this week. One master, several workers. That's the shape.
The way nginx and its modules work is determined in the configuration file. By default the configuration file is named nginx.conf and placed in the directory /usr/local/nginx/conf, /etc/nginx, or /usr/local/etc/nginx. A simple directive consists of the name and parameters separated by spaces and ends with a semicolon. A block directive ends with braces instead. Per the nginx official Beginner's Guide.
nginx.conf, the tree
Let's say you open /etc/nginx/nginx.conf for the first time and it's just... text. It's simpler than it looks.
Everything nginx does lives in this file. A simple directive is a name and parameters, separated by spaces, ending in a semicolon. A block directive swaps the semicolon for braces, and if it can hold other directives it's called a context: events, http, server, location. The stack is main, then http, then server, then location. After a # the rest of a line is a comment.
Name, parameters, semicolon. Or braces.
Open your nginx.conf this week and find the http block. Count the contexts: main, http, server, location.
nginx -t tests the configuration. The reload signal makes the master process check the syntax validity of the new configuration file and try to apply it. If this is a success, the master process starts new worker processes and sends messages to old worker processes requesting them to shut down. Otherwise it rolls back and continues with the old configuration. Per the nginx official Beginner's Guide.
nginx -t, then reload
Let's say you edited nginx.conf on a live box at 5pm on a Friday. The habit that saves you is two commands.
First nginx -t: it tests the configuration without applying it. If the syntax is wrong you'll know now, not when the server won't start. Then nginx -s reload: the master checks the syntax again, and if it's valid it starts new workers and asks the old ones to shut down gracefully, finishing the requests they're serving. If the test fails, the master keeps the old configuration running.
-t is free. A broken reload isn't.
This week, before your next reload: nginx -t, read the output, only then nginx -s reload.
Serving
Getting bytes to browsers: where a server listens, how the Host header picks the block, root's path math, the location tournament, try_files, MIME types, and sendfile.
- 01listen 80
- 02location, the search order
listen sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests. Both address and port, or only address or only port can be specified. The default_server parameter marks the default server for that port. Default is listen *:80. Per the nginx http core module documentation.
listen 80
Let's say the site answers on 443 but you're not sure where that's decided. It's the listen line.
listen sets the address and port the server accepts requests on. Address, port, or both: listen 80, listen 127.0.0.1:8080, all valid. Default is *:80. The default_server parameter marks which server block answers when nothing else matches that port, and it's a property of the port, not of the name. For HTTPS you add ssl: listen 443 ssl.
Every server block opens with listen. Read it first.
Find every listen line in your config this week. Know which block is default_server for each port.
The location directive sets configuration depending on a request URI. To find a matching location, nginx first checks prefix locations and selects the longest matching prefix. Then regular expressions are checked in the order of their appearance in the configuration file; the search terminates on the first match. If no regular expression matches, the configuration of the prefix location remembered earlier is used. The = modifier defines an exact match, and ^~ skips the regular expression check. Per the nginx http core module documentation.
location, the search order
Let's say a request matches two location blocks and you can't tell which one wins.
nginx remembers the longest matching prefix, then checks regexes in config order, first match wins and stops. No regex match means the remembered prefix is used. Two modifiers bend it: = is an exact match, ^~ on the longest prefix skips the regex check. Only the URI is tested, never the query string.
Longest prefix, then regexes in order. That's the tournament.
Take one URL you serve and walk it through the order by hand this week. Then check you were right.
The proxy_pass directive sets the protocol and address of a proxied server. If proxy_pass is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive. If proxy_pass is specified without a URI, the request URI is passed in the same form as sent by the client. In these cases when location is specified using a regular expression, proxy_pass should be specified without a URI. Per the nginx proxy module documentation.
proxy_pass http://app:3000
Let's say the app receives /api/users but expected /users. One character decides it.
proxy_pass sends the location's requests onward. With a URI, the part matching the location is replaced: location /api/ plus proxy_pass http://app:3000/ turns /api/users into /users. Without a URI, the request goes as sent. In a regex location the docs are firm: no URI.
The trailing slash is a rewrite. Treat it like one.
Log what your backend actually receives this week. The path you see is the slash's fault.
client_max_body_size sets the maximum allowed size of the client request body. If the size in a request exceeds the configured value, the 413 Request Entity Too Large error is returned to the client. Setting size to 0 disables checking of client request body size. Default is 1m. Please be aware that browsers cannot correctly display this error. Per the nginx http core module documentation.
client_max_body_size 1m
Let's say every upload over one megabyte fails with an error page that explains nothing. That's nginx, not the app.
client_max_body_size caps the request body, and the default is 1m. Over the cap nginx returns 413, and the docs warn that browsers cannot correctly display this error. Set it where uploads land; 0 disables the check.
Uploads break at 1m by default. Nobody set it, so you must.
Check client_max_body_size everywhere uploads flow this week. Then set it on purpose, with a number.