Most confusing NGINX behaviour comes from a different location matching than the one you expected.
A server usually contains several location blocks, and exactly one handles each request. The modifiers are:
location = /path: exact match.location /prefix: prefix match.location ^~ /prefix: prefix match that, if it is the longest, disables regex checking.location ~ regex(case-sensitive) andlocation ~* regex(case-insensitive).
The algorithm, which does not simply follow file order:
- If an
=location matches exactly, use it and stop. - Find the longest matching prefix location and remember it. If it has
^~, use it and stop. - Check regular expression locations in the order they appear; the first match wins.
- If no regex matches, use the remembered longest prefix.
So a regex like ~ \.php$ beats the prefix /uploads/ for /uploads/x.php, unless that prefix is written ^~ /uploads/. Order matters only among regexes.
Matching uses the normalized URI path, without the query string. Locations can be nested, and named locations (location @fallback) are used only as targets of try_files or error_page, never matched directly.
Example
server {
listen 80;
location = / { return 200 "exact root\n"; }
location / { return 200 "prefix /\n"; }
location /images/ { return 200 "prefix images\n"; }
location ^~ /static/ { return 200 "static, no regex\n"; }
location ~* \.(png|jpg)$ { return 200 "regex image\n"; }
}
# GET / -> exact root
# GET /about -> prefix /
# GET /images/a.png -> regex image (regex beats a normal prefix)
# GET /static/a.png -> static, no regex (^~ stops regex search)
# GET /images/a.txt -> prefix imagesRemember
- = exact wins immediately; ^~ longest prefix skips regexes
- Otherwise the first matching regex in file order beats prefixes
- Only one location handles a request
Watch out for
- Expecting the longest prefix to win over a regex; regexes are checked after prefixes and win if they match.