Nginx Fundamentals
Nginx Fundamentals
Nginx (pronounced "engine-x") is the web server of choice at nearly every major internet company — serving as the front door for Netflix, Cloudflare, Dropbox, GitHub, and most of the world's highest-traffic sites. Understanding Nginx deeply, from installation to its internal configuration model, is a prerequisite for every production DevOps role. This lesson covers the full mental model: how to get Nginx running, how its configuration file is structured, and the two concepts you will configure hundreds of times in your career — server blocks and location blocks.
Installing Nginx
On modern Ubuntu/Debian systems, the recommended installation path is the official Nginx stable or mainline package from the Nginx APT repository rather than the distribution-default package. The distribution package often lags months behind in security and feature patches.
apt upgrade never accidentally downgrades back to the distro package.The nginx.conf Anatomy
Nginx's entire behavior is driven by a single structured configuration language. The file at /etc/nginx/nginx.conf is the root. Understanding its layered context model is the most important conceptual step.
Configuration is organized into contexts (blocks delimited by { }), and directives in a context apply to that context and all contexts nested within it. There are four you will use constantly:
main— the global context (no surrounding braces). Controls worker processes, PID file, error log, and module loading. Applies to the entire process.events— tuning for the connection-processing model. Key directive:worker_connections.http— everything related to HTTP/HTTPS. Contains server blocks and sets HTTP-wide defaults (MIME types, logging, compression, timeouts, upstream pools).server— a virtual host (equivalent to Apache's VirtualHost). Lives insidehttp. You will have one per domain (or per port).location— matches a URL path pattern inside a server block. This is where most per-route logic lives.
The canonical starting nginx.conf that top companies use in production is much leaner than the default Nginx ships with. Start with this skeleton:
worker_processes auto makes Nginx spawn exactly one worker per logical CPU core. Each worker is single-threaded and handles thousands of connections asynchronously using the epoll (Linux) or kqueue (macOS/BSD) event loop. This is fundamentally different from Apache's thread-per-connection model and is the reason Nginx uses a fraction of the memory under high concurrency.Server Blocks — Virtual Hosting
A server block is Nginx's unit of virtual hosting. Each block answers requests for a specific combination of IP address, port, and Host header (the server_name). When a request arrives, Nginx selects the correct server block through a specific matching algorithm:
- Match the
listenaddress and port exactly. - Among matching blocks, find one whose
server_namematches theHostheader exactly. - If no exact match, check for a leading wildcard (
*.example.com), then a trailing wildcard. - If still no match, use regex server names in order of definition.
- Fall back to the
default_serveron that port.
In production you will always explicitly mark one server block as default_server on port 80 and 443, and that block should return 444 (Nginx closes the connection without a response) for unrecognized hostnames — this prevents scanners from fingerprinting your backend by hitting you by IP.
default_server catch-all block. Without it, the first server block defined on a port becomes the implicit default. This means a scanner hitting your server by IP address will get a response from your first virtual host — potentially leaking its domain name, TLS certificate, or backend technology stack. Always define an explicit return 444 catch-all.Location Blocks — URL Routing
Inside a server block, location blocks route requests to different handlers based on the URL path. Nginx uses a specific precedence algorithm to select the winning location — getting this wrong is one of the most common production configuration bugs.
Location modifiers (in order of precedence):
= /exact— exact string match; highest priority, stops search immediately.^~ /prefix— prefix match that wins over any regex; if matched, stops regex search.~ pattern— case-sensitive regex (first match wins among regexes).~* pattern— case-insensitive regex./prefix— longest prefix match (no modifier); lowest priority, used only if no regex matches.
try_files: The directive try_files $uri $uri/ /index.php?$query_string is the foundation of every Laravel, Symfony, or single-page app deployment. Nginx first checks if the URI maps to a real file, then a real directory, and only falls through to the PHP front-controller when neither exists. This means static files are served by Nginx's C code directly — PHP never wakes up for a .js or .css request.Testing and Reloading Configuration
Always test configuration before applying it. A syntax error in a live nginx.conf will cause nginx -s reload to refuse the change — but an error introduced while the process is down will prevent startup entirely.
nginx -t as a pre-flight check before the pipeline does anything destructive. A failed config test should abort the deploy. At companies like Cloudflare, Nginx config changes go through the same code-review and automated validation pipeline as application code — a bad config that reaches production is a P0 incident.How Nginx Selects a Server Block and Location: A Request Walk-Through
Tracing a single request through the Nginx decision tree builds the mental model you need to debug production issues quickly. Given a request for https://example.com/api/users?page=2:
- The TCP connection arrives on port 443. Nginx looks at all server blocks with
listen 443. - The TLS handshake happens; SNI provides the hostname
example.com. - Nginx scans
server_namedirectives: exact match wins over wildcard. - Inside the selected server block, Nginx evaluates all location blocks. The path is
/api/users. There is no exact match (= /api/users), no^~prefix, and no matching regex. The longest prefix match is/api/. - The
/api/location proxies to the upstream application server. - Nginx sets proxy headers (
X-Forwarded-For,X-Real-IP,Host) and forwards the request over an internal TCP or Unix socket connection. - The upstream response returns; Nginx streams it to the client and logs the request.
proxy_pass: proxy_pass http://backend:3000/ (with trailing slash) strips the location prefix from the URI before forwarding. proxy_pass http://backend:3000 (without) preserves it. A request to /api/users with location /api/ goes to http://backend:3000/users (trailing slash) or http://backend:3000/api/users (no slash). This mismatch causes 404s that are very hard to spot without reading the upstream access logs.Key Variables and Built-In Directives
Nginx exposes dozens of variables in location context. The ones you will use constantly:
$uri— the current normalized request URI (without query string).$args/$query_string— the query string portion.$request_uri— the full original URI including query string (not normalized).$remote_addr— client IP (but this is the load balancer IP behind a proxy; use$http_x_forwarded_forafter trust validation).$host— value of theHostheader, or server name if missing.$scheme—httporhttps.$document_root— therootdirective value for the current location.
By the end of this lesson you should be able to install Nginx from the official repository, read and write a well-structured nginx.conf, create virtual hosts with appropriate server blocks, and route requests through location blocks using the correct modifier for each use case. The next lesson builds on this to cover serving static files, PHP-FPM integration, and application backends in detail.