Location matching
Scope: how nginx chooses which location block
handles a request — the single most-questioned piece of
nginx behaviour, proven here with fixtures instead of
asserted.
The contestants
A request path can match several locations at once.
The lab server carries one of each kind, and each
answers with its own name:
location = /match { return 200 "winner: exact match =\n"; }
location /match { return 200 "winner: plain prefix /match\n"; }
location /match/deep/ { return 200 "winner: longer prefix /match/deep/\n"; }
location ^~ /match/anchored/ { return 200 "winner: caret-tilde prefix ^~\n"; }
location ~ ^/match/.*\.txt$ { return 200 "winner: regex ~\n"; }
The results
$ curl -s http://127.0.0.1:8080/match
winner: exact match =
$ curl -s http://127.0.0.1:8080/match/other
winner: plain prefix /match
$ curl -s http://127.0.0.1:8080/match/deep/x
winner: longer prefix /match/deep/
$ curl -s http://127.0.0.1:8080/match/deep/file.txt
winner: regex ~
$ curl -s http://127.0.0.1:8080/match/anchored/file.txt
winner: caret-tilde prefix ^~
The order that produces them
=exact match. If alocation =spells the request path precisely, it wins immediately. (/match— exact wins even though prefix and longer candidates exist.)- Longest prefix. Otherwise nginx finds the longest
matching prefix
location. (/match/other→ the plain prefix;/match/deep/x→ the longer one.) ^~stops the contest. If that longest prefix carries^~, it wins and regexes are never consulted. (/match/anchored/file.txtends in.txt— the regex would have matched, but^~means "this prefix, and we're done.")- Regexes, in file order. Without
^~, the regex locations (~case-sensitive,~*insensitive) are tried top to bottom, and the first match wins — overruling the longest prefix. (/match/deep/file.txtmatched the regex despite/match/deep/being the longer prefix.) - Fallback. No regex matched? The longest prefix from step 2 serves after all.
Location order in the file matters only among
regexes; moving prefix blocks around changes nothing.
And nested locations and the location / catch-all fit
the same scheme: / is simply the shortest possible
prefix, the fallback of last resort.
Why this matters in practice
Two everyday consequences:
- Static-asset shortcuts. The
^~form exists for "serve these files and skip the regex machinery":location ^~ /static/ { ... }both wins and is fast. - The regex ambush. A broad regex early in the file
(say, one for
.php) will capture requests you expected a prefix block to handle, because step 4 outranks step 2. When a location behaves as if it doesn't exist, check for a regex above it before you check the block itself.
Check: with the rules above, predict the winner for
/match/anchored/other (no .txt) and for /MATCH —
then, if you have the lab, run them. (Answers:
^~ prefix — the regex wouldn't match anyway, and the
prefix contest already ended; and the plain / fallback
of the server — matching is case-sensitive, so nothing
named here matches /MATCH at all.)