nginx - locationの文字列と正規表現の優先順位

少しはまった。正規表現の設定のほうが後で評価されて、設定が上書きされるのか。そういえば、apacheもそうだったかな?

URL: /fooにaliasを設定しているとする。で、/foo以外の画像(正規表現で指定)へのリクエストはreverse proxyしたいとする(/foo/bar.jpgはreverse proxyさせずにaliasで設定しているdirectoryから配信したい)。
この場合、/fooの設定は"location /foo"ではなくて、"location ^~ /foo"じゃないとダメ。

###### 設定A
# location /foo {    # 間違い
location ^~ /foo {
  alias  /path/to/foo;
  index  index.html index.htm;
}

###### 設定B
location ~ .+\.(jpg|gif|png)$ {
  proxy http://image.example.com/;
}

"location /foo"だと"/foo/bar.jpg"のリクエストに対して、その後の設定Bの正規表現のlocationで上書きされて、reverse proxy先のサーバから応答が返ってきた。

おや?と思ったのでnginxのwikiを読むと、locationを読む順番は

  1. リテラル文字列(not 正規表現)のlocation
  2. 正規表現のlocation

となっていた。じゃあ、なぜ"location /foo"ではダメで、"location ^~ /foo"だとOKなのか。

2種類のprefix

locationが反映される順番を変更できる2つのprefix。

"location = /path"

指定した文字列と完全一致するリクエストが適用される。マッチすれば、その後の正規表現はチェックされない。なので、wiki

For example, if the request "/" occurs frequently, then using "location = /" will expedite the processing of this request.

こう記述されてるように、"/"へのアクセスは"location = /"が適用されて終了するので、頻繁に"/"にリクエストが来るなら、これを設定したほうがパフォーマンス上がるよっていうこと。

"location ^~ /path"

指定した文字列と前方一致するリクエストが適用される。マッチすれば、その後の正規表現はチェックされない。上に書いた例ではこれを使ったので、"/foo/bar.jpg"へのリクエストは設定Aが適用されてそこで終わり。reverse proxyには行かない。


wikiにまとめが書いていた。

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.

apacheの例

apacheでも

Alias /foo /path/to/foo
<Location /foo>
  # 設定A
</Location>

<LocationMatch .+\.(jpg|png|gif)$>
  # 設定B
</LocationMatch>

と書いた場合、/foo/bar.jpgへのリクエストには設定Bが適用される。はず。

追記

apacheの場合はLocationとLocationMatch自体には優先順位はなくて、後から書いた設定が上書きをするだけだった。