How to control NGINX 'Location' directive matching order?

leeoniya picture leeoniya · Dec 8, 2011 · Viewed 19.4k times · Source

I'm trying to optimize my 'location' directives and cannot find a good way of determining if a specific location match is even attempted. Using echo inside the location block doesn't help here.

The NGINX ngx_http_core_module documentation is somewhat confusing.

To use regular expressions, you must use a prefix:

  1. ~ For case sensitive matching

  2. ~* For case insensitive matching

How the match is performed:

  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.

Number 2 here says "conventional strings" but then says it can be used with the ^~ prefix. Doesn't ~ imply a RegExp? If not, how does it determine what is an isn't a RegExp?

Specifically, I want the following:

  1. Serve anything out of literal /assets directly. STOP SEARCH.

  2. Serve anything matching RegExp \.php$|/$ via fast-CGI STOP SEARCH.

  3. Serve everything else directly via literal /

This way, there is only a / match attempt for non-dynamic files served from outside of assets.

I have:

location ^~ /assets {}      # search-terminating literal? or regex?
location ~ \.php$|/$ {}
location / {}               # is this match always attempted?

From the document, it looks as though the actual order would be 1-3-2, always running the literal / match. Yes, this optimization won't make any difference for real performance, but I just want to clear up some ambiguity.

Answer

m33lky picture m33lky · Dec 9, 2011

From the wiki:

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 ] 
}

So, this will be matched first: location ~ \.php$ {}

Even though assets are served out of location / {}

Inside the php block you also want to secure against malicious uploads before passing to fastcgi:

if ($uri ~* "^/uploads/") {
  return 404;
}

As you can see nginx works a little bit differently than you might expect.