こんにちは。寺岡です。
最近WEBサーバにnginxを利用することが多いのですが、
その時にハマったポイントをご紹介します。
nginx には ifディレクティブがあり、条件によって設定や変数を切り替えることができます。
このifディレクティブはかなりの曲者で、一般的な感覚とは異なる挙動をします。
皆さんは、以下の設定でどんなヘッダが出力されると思いますか?
1 2 3 4 5 6 7 8 9 10 |
location /only-one-if { set $true 1; if ($true) { add_header X-First 1; } if ($true) { add_header X-Second 2; } return 204; } |
一般的な感覚だと、X-First: 1とX-Second: 2が出力されそうな気がします。
当然そうなるんだとろうと思って実行してみると……。
1 2 3 4 5 6 7 8 |
$telnet localhost 80 GET /only-one-if HTTP/1.0 HTTP/1.1 204 No Content Server: nginx Date: Wed, 31 Oct 2012 01:01:01 GMT Connection: close X-Second: 2 |
驚いたことに、出力されるのはX-Second: 2のみとなります。
実はこのifディレクティブ、本家でも IfIsEvil なんて注意されていたりします。
設定次第ではSIGSEGVが発生する場合もあるので要注意です。
挙動の詳細については下のリンク先で詳しく説明されています。
How nginx "location if" works
正確に理解できているか自信はありませんが、おそらく以下のようなルールになっていると思われます。
- if はrewirteフェーズに上から順に実行される
- add_header等のcontent handlerと呼ばれる種類の定義は、親子関係を持つブロック間で継承される
- location内にifがある場合、最も深いブロックのcontent handlerが適用される(同一階層の場合は下に記述されたものが優先される)
- returnやbreakがある場合、rewirteフェーズを終了し、その時点で有効なcontent halderが適用される
この挙動はlocationコンテキスト内にifディレクティブを記述した場合に発生するようです。
ファイルの存在有無で挙動を変えたい場合は try_files を活用すれば何とかなりますので、
よほどの理由がない限りlocation内のifディレクティブは禁止したほうが良いでしょう。