338

What order do location directives fire in?

1
  • Neither the existing answers nor the official nginx documentation explain how the location selection algorithm works when nested location blocks are present in the configuration. A detailed explanation of this algorithm can be found in this ServerFault Q/A. Commented Jul 2 at 19:22

4 Answers 4

514

From the HTTP core module docs:

  1. Directives with the "=" prefix that match the query exactly. If found, searching stops.
  2. All remaining directives with conventional strings. If this match used the "^~" prefix, searching stops.
  3. Regular expressions, in the order they are defined in the configuration file.
  4. If #3 yielded a match, that result is used. Otherwise, the match from #2 is used.

Example from the documentation:

location  = / {
  # matches the query / only.
  [ configuration A ] 
}
location  / {
  # matches any query, since all queries begin with /, but regular
  # expressions and any longer conventional blocks will be
  # matched first.
  [ configuration B ] 
}
location /documents/ {
  # matches any query beginning with /documents/ and continues searching,
  # so regular expressions will be checked. This will be matched only if
  # regular expressions don't find a match.
  [ configuration C ] 
}
location ^~ /images/ {
  # matches any query beginning with /images/ and halts searching,
  # so regular expressions will not be checked.
  [ configuration D ] 
}
location ~* \.(gif|jpg|jpeg)$ {
  # matches any request ending in gif, jpg, or jpeg. However, all
  # requests to the /images/ directory will be handled by
  # Configuration D.   
  [ configuration E ] 
}

If it's still confusing, here's a longer explanation.

Sign up to request clarification or add additional context in comments.

6 Comments

Note that both / and /documents/ rules match the request /documents/index.html, but the latter rule takes precedence since it's the longest rule.
Important caveat: location /test/ will be magically half-used (proxy_pass, add_header etc will be evaluated, but setting variables will not) over location ~* ^/test$ for requests to /test and will return a redirect, only location = /test can overrule this.
An interesting observation. This behavior is described in the documentation, which mentions that one of the proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass directives should be used as the location content handler to trigger this behavior. However, the fact that it cannot be overridden with location ~ ^/test$ { ... } surprises me as well. This is probably due to the fact that "exact match" locations are checked first, while regex locations are evaluated last — after all prefix locations have been tested.
I don't understand — what do you mean by saying "add_header will be evaluated, but setting variables will not"?
It's been two years, I don't remember, but I vaguely remember running into this "surprising feature" at the time. I may have meant that while the location will be used if one of those methods is handling it, values you set inside of it will not be kept if it does not. I do set some variables in locations to store e.g. which location handled the request, and include them in logs, and I did have some issues where they were not set during logging. It could be that, but I'm not sure.
162

It fires in this order.

  1. = (exactly)

location = /path

  1. ^~ (forward match)

location ^~ /path

  1. ~ , ~* (regular expression case sensitive and insensitive)

location ~ /path/ , location ~* .(jpg|png|bmp)

  1. /

location /path

4 Comments

Leaving off the the trailing slash will match more than just exact. #1 should be location = /path/, and the others should include start and end modifiers (^ and $)
location = /path matches to domain.com/path, and location = /path/ to domain.com/path/. Others don't need start and end modifiers.
where does backward match fits in order
@Yusuf use regular expression for that.
23

Locations are evaluated in this order:

  1. location = /path/file.ext {} Exact match
  2. location ^~ /path/ {} Priority prefix match -> longest first
  3. location ~ /Paths?/ {} (case-sensitive regexp) and location ~* /paths?/ {} (case-insensitive regexp) -> first match
  4. location /path/ {} Prefix match -> longest first

The priority prefix match (number 2) is exactly as the common prefix match (number 4), but has priority over any regexp.

For both prefix matche types the longest match wins.

Case-sensitive and case-insensitive have the same priority. Evaluation stops at the first matching rule.

Documentation says that all prefix rules are evaluated before any regexp, but if one regexp matches then no standard prefix rule is used. That's a little bit confusing and does not change anything for the priority order reported above.

Comments

1

Nginx configuration is declarative and has very little in common with imperative programming languages (with the notable exception of directives from the rewrite module, which are internally translated into pseudo-code and executed during each request's processing). Everything else is compiled into the internal configuration representation. There is no such thing as a "location directive firing order".

There is, however, a well-defined location selection algorithm. Only the most upvoted answer on this page attempts to describe that algorithm, although it still omits several important technical details. Other answers mistakenly try to describe a non-existent "evaluation order".

There are only three types of location blocks:

  • Exact match
  • Prefix match
  • Regular expression (regex) match

The location selection algorithm works as follows:

  1. Exact match:
    If a matching "exact match" location (declared using the = modifier) exists, it is selected immediately to handle the request. Otherwise, the algorithm proceeds to step 2.

  2. Prefix match:
    The longest matching prefix location is identified. All prefix locations (both with and without the ^~ modifier) are internally stored in a data structure known as a red-black tree when the configuration is compiled at nginx startup. The longest matching prefix is found using this binary tree traversal with time complexity O(log n). If the longest matching prefix has the ^~ modifier, it is selected to handle the request. Otherwise, proceed to step 3.

  3. Regex match:
    All regular expression locations, both case-sensitive (defined using the ~ modifier) and case-insensitive (defined using the ~* modifier) are checked sequentially in the order they appear in the configuration. This is the only case where declaration order matters. The first regex location that matches the request URI is selected to handle the request. If no regex matches are found, the longest matching prefix location from step 2 is used.

As you can see, the concept of "order" only applies to step 3, where regex locations are tested in declaration order. If no exact match location is found, nginx will search for the longest matching prefix location, regardless of whether any regex locations are defined. And if the best matching prefix location does not have the ^~ modifier, nginx will try to match the request that will eventually be processed by that location against every defined regex location pattern. This can be a performance concern when many are defined, as regex matching is relatively expensive compared to every other operation described above.

Nginx was originally developed for high-load systems, and this algorithm is designed to help write efficient configurations by minimizing the number of regex pattern-matching operations. Even more control is possible by using nested locations. Typically, they are used to isolate a group of nested regex locations under the outer prefix location.

Things get significantly more complicated when the nested location blocks are involved, and even the official nginx documentation does not fully describe how location selection works in these cases. I have a separate answer on ServerFault that explains the location selection algorithm when nested locations are used.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.