How NGINX prioritises locations

Subtile nuances in configuring your NGINX location blocks

Geschrieben von Timo Rieber am 14. Oktober 2023

Today I stumbled upon some subtile nuances in configuring locations in NGINX. I'm not an expert in NGINX, but I've been using it for a while now and read some of the documentation. I thought I understood how NGINX prioritises locations, but I was wrong.

My innocent assumption was that NGINX would process the locations in their given order and then use the first match, like the Django URL dispatcher processes requests. Indeed, NGINX does not necessarily stop at the first match, but processes more locations and then uses the most specific match.

I wanted to serve the favicons for our texsite CMS from a different location than the other static files to allow simple customisation per tenant. The first try and many variants looked like this:

location /static/favicon/ {
  alias /var/www/texsite/$server_name/;
}

location ~ ^/(?:media|static)/ {
  root /var/www/texsite;
}
Nginx

Here the first location matches all requests starting with /static/favicon/ and serves the files from the tenant specific directory. The second location matches all requests starting with /media/ or /static/ and serves the files from the tenant independent directory. Both locations get evaluated, but the second location is a regular expression and takes precedence over the so called prefix location. So the first location will never be used.

The solution is simple, but very subtile. You need to add ^~ to the prefix location, that it is prioritised over the regular expression location.

The final configuration looks like this:

location ^~ /static/favicon/ {
  alias /var/www/texsite/$server_name/;
}

location ~ ^/(?:media|static)/ {
  root /var/www/texsite;
}
Nginx

An answer on StackOverflow helped me to understand the priorities and find the solution. According to this answer and the NGINX docs it works like this:

  1. Prioritise directives with the = modifier. If one matches the query exactly, use it.
  2. Check all remaining directives with conventional strings. If there are matches using the ^~ prefix, searching stops processing to most specific.
  3. Evaluate regular expressions, in the order they are defined in the configuration file.
  4. If #3 yielded a match, again the most specific result is used. Otherwise, the most specific match from #2 is used.