skip to content

Server and Location Blocks

How a request finds its handler: listen and server_name pick the virtual host, then location matching runs in a defined order — exact, prefix, regex. Interviewers ask about that order and about root versus alias, the classic off-by-one-path bug.

on this pageshow

questions

5

In nginx, several `location` blocks in one server could match the same request path. In what order does nginx evaluate exact (`=`), prefix, `^~` and regular-expression locations, and which one ends up handling the request?

level: middleimportance: must knowfreq 72%

answer

  1. file order is not match order
  2. one location wins, never two
  3. longest prefix is only remembered
  4. ^~ ends the regex phase
  5. regexes tried top to bottom

basics

~20 s

Nginx tries exact (=) locations first. Otherwise it remembers the longest matching prefix: a ^~ prefix wins immediately; otherwise regular expressions are tested in configuration-file order and the first match wins, falling back to that remembered prefix.

solid answer

~40 s

Nginx picks exactly one location per request, and the order is not the order of the file. First it looks for an exact match — `location = /health` — and if one matches, that location serves the request and the search stops. Otherwise nginx scans all prefix locations and *remembers* the longest one that matches. If that longest prefix carries the `^~` modifier, nginx stops there and never evaluates regexes. Otherwise it tests regular-expression locations (`~` case-sensitive, `~*` case-insensitive) in the order they appear in the config, and the first regex that matches wins. Only if no regex matches does the remembered prefix serve the request. The practical consequence: a short regex anywhere in the file can steal traffic from a much longer, more specific prefix, and `^~` is how you protect that prefix.

code

nginx · 21 lines
nginx
server {
    listen 80;
    server_name example.com;

    location = /health {
        return 200 "ok\n";
    }

    location /docs/ {
        root /srv/site;
    }

    location ~* \.(png|jpg|css)$ {
        expires 30d;
        root /srv/site;
    }

    location / {
        return 404;
    }
}

go deeper

for a junior

Know that nginx picks one location per request and that = means an exact path match. Be able to point at the catch-all location / and say it is the last resort, not the first thing tried.

for a middle

Explain all five steps in order and, crucially, that a plain longest prefix is only remembered while regexes are evaluated first. Name ^~ as the modifier that ends the regex phase.

for a senior

Diagnose the real incident: a broad asset regex quietly capturing a directory that needed its own root or headers. Show how you confirmed the winner from logs rather than by reading the file top to bottom.

for a principal

Own the convention. Decide whether the estate routes by prefix with ^~ on protected trees or leans on regexes, and argue the cost: regexes are order-dependent across included files, so they make a large config's behaviour depend on include order.

## What "matching" means in nginx Once nginx has chosen a `server` block for a request, it chooses exactly **one** `location` block inside it. Locations are not layered and not merged: the winner's directives (plus whatever it inherits from `server` and `http`) become the configuration for that request. So "which location wins" is not a style question — it decides which root, which handler, which headers and which limits apply. ## The algorithm, step by step 1. **Exact match.** If a `location = /uri` matches the request path character for character, that location is used and the search **stops immediately**. This is why `= /` or `= /health` is the cheapest possible route. 2. **Prefix scan.** Nginx tests every prefix location (no modifier, or `^~`) and *remembers* the **longest** one that matches. It does not use it yet. 3. **The `^~` short-circuit.** If the remembered longest prefix was declared with `^~`, nginx stops here and uses it — regular expressions are never evaluated. 4. **Regular expressions.** Otherwise nginx tests `~` (case-sensitive) and `~*` (case-insensitive) locations **in the order they appear in the configuration file**, top to bottom. The **first** one that matches wins outright. 5. **Fallback.** If no regex matched, the prefix remembered in step 2 is used. ```nginx server { location = /health { return 200 "ok\n"; } # 1: exact, stops the search location ^~ /assets/ { root /srv/site; } # 3: prefix that blocks regexes location ~* \.(png|jpg)$ { expires 30d; } # 4: regexes, in file order location /docs/ { root /srv/site; } # 2: plain prefix, only remembered location / { return 404; } # catch-all prefix } ``` ## The trap candidates fall into Given `location /docs/` and `location ~* \.png$`, a request for `/docs/diagram.png` is handled by the **regex**, not by the longer prefix — because step 2 only *remembers* the prefix and step 4 runs before step 5. Teams discover this when a global `\.(png|jpg|css)$` block silently takes over a directory that was supposed to have its own root, auth or caching rules. The fix is either to declare the directory as `location ^~ /docs/` so the regex phase is skipped, or to narrow the regex so it no longer covers that path. The mirror-image trap is assuming file order matters for prefixes. It does not: prefixes are compared by length, so moving `location /` to the top of the file changes nothing. File order matters **only** among regexes. ## Modifiers worth naming precisely - *(none)* — plain prefix; competes on length, evaluated last. - `=` — exact; the whole normalized path must equal the value. - `^~` — prefix that suppresses the regex phase if it is the longest match. It is **not** a regex, and the `^` is not an anchor. - `~` / `~*` — POSIX regex, case-sensitive / case-insensitive. - `@name` — a *named* location. It does not take part in this search at all; it is reachable only through an internal redirect from `try_files`, `error_page` and similar. ## Nesting A prefix location may contain nested locations. Once the outer location is selected, nginx repeats the same search among the nested ones. Nesting is a scoping tool, not a way to make two locations both apply. ## Proving which one matched The cheapest reliable technique is to tag each location and log the tag: ```nginx log_format tagged '$remote_addr "$request" loc=$loc'; access_log /var/log/nginx/access.log tagged; location /docs/ { set $loc docs; ... } location ~* \.png$ { set $loc png; ... } ``` On a binary built with `--with-debug` you can also set `error_log ... debug;` and read the `test location:` lines nginx emits while it walks the tree. Guessing from the file top to bottom is exactly the habit the question is testing for.

  • Where do nested locations fit into that order?
    A prefix location can contain locations of its own. Nginx first runs the normal search at the outer level; if the winner is a prefix location containing nested locations, it repeats the same algorithm among them. Nesting narrows the search after a winner is chosen — it never lets two locations both contribute directives to one request.
  • Does a named location such as `@fallback` compete in this matching at all?
    No. Named locations are excluded from request matching entirely — no request path ever selects one. They exist only as internal redirect targets, reached from the last argument of `try_files`, from `error_page`, and from a few similar directives. That is why a named location can safely contain a handler that would otherwise loop.
  • How would you prove which location actually handled a given request in production?
    Add `set $loc <tag>;` inside each candidate location and include `$loc` in a custom `log_format`, then read the access log. On a debug-enabled build you can also raise `error_log` to `debug` and read the `test location:` lines, but that is heavy for a busy server — the tagged access log costs almost nothing.

saying these in an interview costs you the question

  • Says the first matching location in the file wins
  • Thinks the longest prefix always beats a regex
  • Believes several locations can both apply to one request
  • Reads ^~ as a case-insensitive or anchored regex
  • Thinks reordering prefix locations changes which one matches

context

open as a page

In an nginx `location` block, what is the difference between the `root` and `alias` directives, and what filesystem path does `location /images/ { alias /data/pics; }` produce for a request to /images/logo.png?

level: middleimportance: must knowfreq 60%

basics

~20 s

Root appends the whole request path to its value; alias replaces the matched location prefix with its value. With alias /data/pics and location /images/, the request /images/logo.png becomes /data/picslogo.png, because the missing trailing slash concatenates the two.

open as a page

An nginx instance has several `server` blocks that all declare `listen 80;` with different `server_name` values. How does nginx decide which block handles an incoming request, and what happens when the request's Host header matches no `server_name` at all?

level: juniorimportance: should knowfreq 62%

basics

~20 s

Nginx first narrows candidates by the listen address and port, then matches the request's Host header against server_name. If no name matches, the request goes to that socket's default server: the block whose listen carries default_server, or otherwise the first such block in the configuration.

open as a page

An nginx site serving a single-page app uses `try_files $uri $uri/ /index.html;` and its error log fills with "rewrite or internal redirection cycle while internally redirecting to /index.html". What does `try_files` actually do, and what produces that cycle?

level: seniorimportance: should knowfreq 46%

basics

~20 s

try_files tests each argument as a file or directory under the current root and serves the first that exists; the last argument is a fallback URI that triggers an internal redirect. If that fallback file is missing, the redirect re-enters the same location and loops until nginx gives up with a 500.

open as a page

In nginx, a `location /search?q=` block never matches anything, and the value logged as `$request_uri` often differs from the path a location matched. What string does nginx actually compare `location` prefixes and regexes against?

level: seniorimportance: nice to knowfreq 33%

basics

~20 s

Nginx matches locations against the normalized request path: percent-decoded, with dot and dot-dot segments resolved and repeated slashes merged, and with the query string removed. That value is $uri; $request_uri holds the raw original line including the query.

open as a page