What location matching decides
When a request arrives, nginx must choose exactly one location block to handle
it. The rules are not simply “first match wins” or “longest wins” — there is a
specific priority order that mixes modifiers (=, ^~, ~, ~*, none),
prefix length, and file order for regexes. Misunderstanding this order is
the source of countless “why is the wrong block serving my request” bugs.
How it works
nginx resolves a location in this order:
- Exact
location = /path— if it matches, stop. Highest priority. - Find the longest matching plain prefix (
location /pathandlocation ^~ /path). Remember it. - If that longest prefix used
^~, select it and skip all regex. - Otherwise evaluate regex locations (
~case-sensitive,~*case-insensitive) in file order; the first to match wins. - If no regex matches, fall back to the longest plain prefix from step 2.
So exact beats preferential prefix, which beats regex, which beats an ordinary prefix. The tester below runs this exact algorithm against any path and your set of blocks.
Modifier quick reference
| Modifier | Type | Priority | Notes |
|---|---|---|---|
= | Exact | 1st (highest) | Stops matching immediately on hit |
^~ | Preferential prefix | 2nd | Wins if longest prefix; skips all regex |
~ | Case-sensitive regex | 3rd | Tried in file order; first match wins |
~* | Case-insensitive regex | 3rd | Same file-order evaluation as ~ |
| (none) | Plain prefix | 4th (fallback) | Longest-prefix rule applies |
Illustrative worked examples
Example 1 — exact match short-circuits everything.
Suppose your config has:
location = /favicon.ico { ... } # A
location ~* /favicon { ... } # B (regex, case-insensitive)
location /favicon { ... } # C (plain prefix)
A request for /favicon.ico hits block A (exact match) and nginx stops at step 1. Blocks B and C are never evaluated, which is why = is ideal for high-traffic static assets — it avoids scanning the rest of the config on every hit.
Example 2 — ^~ shields a directory from regex.
location ^~ /static/ { root /var/www; } # D
location ~* \.php$ { fastcgi_pass ... ; } # E
A request for /static/js/app.js matches prefix /static/ with ^~. nginx selects D and skips the PHP regex entirely, which is the intended outcome — you don’t want a PHP handler catching static files.
Example 3 — regex vs. plain prefix.
location /api { proxy_pass http://api:3000; } # F (plain prefix)
location ~ ^/api/internal/ { return 403; } # G (regex)
A request for /api/internal/secrets matches both. nginx finds the plain prefix F, but since it has no ^~, it continues to the regex phase. Block G matches first in regex order and returns 403 — even though F matched “longer” as a prefix. The regex wins over the plain prefix when no ^~ is used.
Common mistakes
- Forgetting file order for regexes. Two
~blocks where the more general one appears first will steal matches from the more specific one below it. Put the more specific regex higher in the file. - Trailing slash ambiguity.
location /imagesmatches/imagesfoo;location /images/does not. Omitting the slash when you intend to scope a directory can expose unexpected paths. - Assuming longer regex beats shorter. Regex priority is purely file order; length and specificity are irrelevant unless you use
=or^~. - Mixing
^~and regex expecting the regex to win. Once^~takes the longest prefix, regex blocks are completely skipped for that request.
Tips and notes
Use = for hot single URIs like / or /favicon.ico to short-circuit matching.
Use ^~ /static/ to protect an asset directory from regex handlers. Remember
regexes are tried in the order written, so put more specific regex blocks
above general ones. A trailing slash changes the prefix, so location /api and
location /api/ match different sets of URIs — pick deliberately.