What this tool does

Paste the location lines from a server block plus one request <uri> line, and the matcher runs NGINX's documented selection algorithm in front of you. It names the winning block and shows the whole walk: what happened at each step and why, including the steps that did not decide anything. A query string on the request line is allowed; it is kept out of the match and only feeds $arg_<name> variables.

The rule it exists to teach

NGINX does not pick the first location that matches, and it does not pick the last. It tries an exact match first and stops if one hits. It then checks every prefix location and remembers the longest match, not the first. If that prefix carries ^~ it wins immediately. Otherwise regular expressions are tried in file order and the first one that matches wins, beating the remembered prefix. From nginx 1.31.5, if no regex matches, predicate locations (location $variable) are tried in file order, and the first whose variable is neither empty nor 0 wins. Only if nothing in those two rounds matches does the prefix win after all.

File order decides only the regular-expression and predicate rounds, which is why reading a config from top to bottom actively misleads you about the result.

The two results people meet the hard way

A /images/ prefix block loses to a ~ \.(gif|jpg|png)$ block written below it, because regexes are tried after the prefix round and beat it. The more specific-looking block, written first, is outranked by design.

And ^~ does not mean "higher priority": it means stop before the regular expressions (and, from 1.31.5, before the predicate locations too). That is precisely why it fixes the case above.

What else it reports

Alongside the walk, the tool inspects the configuration itself: prefix blocks a regular expression could take over (with the ^~ fix named), duplicate locations, the absence of a location / catch-all, the nginx version a predicate location needs, and any predicate whose variable has no value.

Honest limits

One server block: no server_name selection and no listen-port matching. The ~ and ~* patterns compile to JavaScript regular expressions; PCRE and JS agree on the syntax used in ordinary location blocks, but they are different engines, so trust NGINX itself for anything exotic. No rewrite, try_files or internal redirects: this answers which block is selected, not what the whole request does. The URI is matched as written, because NGINX decodes before matching and doing half of that here would be worse than doing none. Variables are not evaluated: a $arg_<name> predicate reads the query string, any other variable takes the value you give it on a variable $name value line, and an undeclared variable counts as empty, so its predicate does not match.

Sources