In an nginx configuration file, what are the main, events, http, server and location contexts, and what happens to a directive set in an outer context when an inner context does not repeat it?
answer
- braces create scope, not indentation
- outer level supplies the default
- innermost definition wins
- events for workers, http for serving
- location only inside server
basics
~20 sAn nginx configuration is a tree of nested contexts — main, events, http, server, location — and each nested level inherits the directives of the level above it unless that level sets its own value for the same directive.
solid answer
~50 snginx config is nested blocks, not a flat list. **main** is the top of the file, outside every brace: process-level settings such as `user`, `worker_processes`, `pid` and `error_log`. **events** configures connection processing — `worker_connections` lives only there. **http** holds everything about serving HTTP and is where site-wide defaults such as `keepalive_timeout`, `gzip`, `log_format` and `client_max_body_size` belong, along with `upstream` and `map` blocks. **server** is one virtual host inside `http`, carrying `listen` and `server_name`. **location** is a URI-matching block inside a `server`. Scoping is by nesting: a directive applies to its own block and everything nested below it, and an inner block inherits the outer value unless it declares that directive itself, in which case the inner value wins for that block. Putting a directive in a context where it is not allowed is a startup error; putting it in a legal but wrong context usually just does nothing visible.
code
nginx · 22 linesuser nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
keepalive_timeout 65;
client_max_body_size 1m;
server {
listen 80;
server_name example.com;
location /uploads/ {
client_max_body_size 50m;
}
}
}go deeper
Be able to name the five core contexts and say which settings live in each, and state plainly that an inner block inherits the outer value unless it sets its own.
Explain the difference between simple and block directives, why braces rather than indentation define scope, and why a directive in a legal-but-wrong context fails silently instead of erroring.
Show how you use inheritance deliberately: defaults declared once in http, overrides only where a host genuinely differs, and nginx -T to prove which block a value really came from.
Own the convention: decide what belongs at http level for the whole fleet versus what a team may override per vhost, and treat repeated per-server settings as configuration debt to be pulled upward.
## Contexts are nested scopes An nginx configuration file is a tree of blocks called *contexts*. Each directive is declared by its module as valid in a specific set of contexts, and its effect is scoped to the block it sits in plus everything nested inside that block. Reading a config therefore means reading inwards, not top to bottom. The core contexts, from the outside in: - **main** — the top level of the file, outside all braces. Process-wide settings live here: `user`, `worker_processes`, `pid`, `error_log`, `worker_rlimit_nofile`, and top-level `include` statements. - **events** — a block that configures how connections are accepted and processed: `worker_connections`, `use`, `multi_accept`. - **http** — everything about serving HTTP. This is the natural home for defaults that should apply to every site: `sendfile`, `keepalive_timeout`, `gzip`, `log_format`, `access_log`, `client_max_body_size`, plus the `upstream` and `map` blocks. - **server** — a single virtual host, nested inside `http`, carrying `listen`, `server_name` and any default that applies to that whole host. - **location** — a URI-matching block nested inside a `server` (or inside another `location`), carrying per-path behaviour. Alongside these there are siblings: `stream` sits at main level next to `http` for TCP/UDP proxying, `mail` for mail proxying, and `types`, `if` and `limit_except` appear nested further down. The rule is uniform: a block is a scope. ## Simple directives and block directives A **simple directive** is a name, its arguments, and a semicolon: ```nginx keepalive_timeout 65; ``` A **block directive** takes the same form but ends in braces instead of a semicolon. When a block contains further directives, it is a context: ```nginx http { keepalive_timeout 65; server { listen 80; server_name example.com; location /api/ { client_max_body_size 20m; } } } ``` The braces are the only thing that creates scope. Indentation is a convention for humans; nginx ignores it entirely, which is why a misplaced closing brace can silently move a whole `location` into the wrong `server`. ## Inheritance runs downward only In the example above, the `server` and the `location` both operate with `keepalive_timeout 65`, because neither redefines it. The `location` uses `client_max_body_size 20m`, while every other path in that server uses whatever `http` set (or nginx's compiled-in default of 1m if nothing did). Three consequences follow: 1. **Inheritance is one-way.** Nothing a `location` sets can affect its `server`, and nothing a `server` sets can affect `http`. A directive placed too deep simply does not apply to the requests that never reach that block. 2. **The nearest definition wins.** For ordinary single-value directives, the innermost level that declares the directive determines the value; the outer value is not merged with it, it is simply not consulted. 3. **Defaults belong high.** Anything you want everywhere should be declared once in `http` rather than copy-pasted into every `server`, because every nested level picks it up for free. That is the practical payoff of the model, and it is why config bloat is usually a sign someone did not trust inheritance. A small but important exception: a family of directives that accumulate a list — `add_header` and `proxy_set_header` are the well-known ones — are inherited only if the current level defines *none* of them. Defining one at an inner level discards the whole inherited list rather than adding to it. ## Two very different failure modes Putting a directive somewhere it is not permitted is loud: `nginx -t` reports `"worker_connections" directive is not allowed here` and nginx refuses to start or reload. Putting a directive somewhere legal but ineffective is silent: `client_max_body_size` in a `location` that the request never matches, or a `proxy_read_timeout` on the wrong virtual host, produces no error at all and no behaviour change. Almost every "I changed the config and nothing happened" report is this second case. ## Reading the config you actually have Because real deployments split the tree across `include` files, the file you edited is rarely the whole picture. `nginx -T` validates the configuration and prints the entire resolved tree, includes expanded, so you can see which block a directive really landed in and whether a deeper block overrides it.
- If the same simple directive appears twice inside one context, what does nginx do?For most directives nginx refuses the config with a `"..." directive is duplicate` error at parse time, so a copy-paste mistake in the same block fails fast. A smaller set is defined to accumulate within a level — `add_header`, `proxy_set_header`, `listen` and `error_page` among them — and there repeating the directive adds another entry rather than being an error.
- Where does the `stream` context fit, and why can't you put a `location` in it?`stream` is a top-level context alongside `http`, provided by the stream module, for proxying raw TCP and UDP. It has its own `server` blocks with `listen` and `proxy_pass`, but no `location`, because at that layer nginx never parses a request — there is no URI to match on, only a connection to forward.
- Why is it better to set a common default in `http` than to repeat it in every `server` block?Every nested `server` and `location` inherits it, so there is exactly one place to change it and no chance of one vhost drifting. Repetition also hides intent: a reader cannot tell whether a repeated value is deliberate for that host or leftover copy-paste. Override in a `server` only where that host genuinely differs.
Contexts work like nested CSS rules: a value set on the outer selector applies to everything inside it until a more specific inner rule states its own value.
saying these in an interview costs you the question
- Says nginx reads the config top to bottom like a script
- Puts worker_processes or worker_connections inside the http block
- Thinks a location block can sit directly in http
- Believes indentation determines which block a directive belongs to
- Assumes outer and inner values are merged for every directive