# NGINX location matcher

> Paste your location blocks and a URI, and watch NGINX's five-step selection run: exact, longest prefix, the caret-tilde early exit, regexes in file order, then the fallback.

- Tool: https://ronutz.com/en/tools/nginx-location-matcher
- Family: Networking

---

## 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 of the five steps and why, including the steps that did not decide anything.

## 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. Only if no regex matches does the prefix win after all.

File order decides exactly one of those five steps, 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**. 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, and the absence of a `location /` catch-all.

## 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.

## Standards and references

- [NGINX documentation: ngx_http_core_module, location directive](https://nginx.org/en/docs/http/ngx_http_core_module.html#location) - the selection algorithm: exact match, longest prefix remembered, ^~ suppressing regular expressions, regular expressions in order of appearance, prefix fallback
- [NGINX documentation: how NGINX processes a request](https://nginx.org/en/docs/http/request_processing.html) - server and location selection order for an incoming request

## Related reading

- [Limiting requests, connections and bandwidth in NGINX: leaky buckets and the burst that surprises people](https://ronutz.com/en/learn/nginx-limiting-connections-and-rate.md): Three different limits, three different directives, and one shared mechanism worth understanding before you tune it. The request limiter is a leaky bucket, not a quota per second, and burst plus nodelay change its behaviour in ways the names do not suggest.
- [NGINX location matching: why the block you expected is not the one that ran](https://ronutz.com/en/learn/nginx-location-matching-order.md): NGINX does not pick the first location that matches, and it does not pick the last. It follows a fixed five-step order in which the file's own order matters for exactly one step, which is why reading a config top to bottom will mislead you about which block wins.
- [Reloading NGINX without dropping traffic, and the first four things to check when it breaks](https://ronutz.com/en/learn/nginx-reload-signals-and-first-troubleshooting.md): A reload is not a restart: the master validates the new configuration, starts new workers, and lets the old ones finish what they were doing. Knowing that, and knowing which log answers which question, resolves most NGINX problems before they become interesting.
- [The NGINX configuration tree: what includes, in what order, and who owns the worker](https://ronutz.com/en/learn/nginx-configuration-tree-and-includes.md): One file, a directory of fragments, and an include order that decides which directive wins. Plus the two ownership questions that explain most permission failures: which user the master runs as, which user the workers run as, and why those are deliberately different.
- [The NGINX proxy_pass trailing slash: one character that decides what your backend receives](https://ronutz.com/en/learn/nginx-proxy-pass-uri-rewriting.md): proxy_pass with a URI part replaces the matched location prefix. Without one, the original request URI passes straight through. That is the entire rule, it is a binary switch rather than a shade of difference, and a single slash is enough to flip it.
