軽くNGINXを使う機会があった。
NGINXの設定ファイルではif文が使える!便利!
と思ったら、公式がIf is Evil... when used in location contextという記事を出している。。。
怖い。。。
というわけでこの記事を読み解く。。。
簡単に言うと。と言うかはじめに書いてあるが、
「If文はlocationディレクティブ内で使用すると問題が発生することがある。期待しない動作をすることや場合によってはsegfaultすることもある。できるなら使わないで。」ってさ。
なんだよその機能。ガクブル :(;゙゚'ω゚'):
代わりにtry_files, return, rewrite...lastを使用しろってさ。
場合によってはembeded perlや3rd Party Moduleを使用しろと。
以下のようないくつかおかしな挙動をする例が紹介されている。
ifとadd_headerの組み合わせ
location /only-one-if {
set $true 1;
set $a 0;
add_header X-First 1;
if ($true) {
add_header X-Second 2;
}
if ($true) {
add_header X-Third 3;
}
add_header X-Fourth 4;
return 204;
}
コレはX-Thirdしかヘッダーが追加されない。
謎仕様。
最後のifが優先され、if外のadd_headerは無視される。
もちろん2つのif文をなくせばX-First, X-Fourthの2つが追加される。
proxy_passの無視
location /proxy-pass-uri {
proxy_pass http://127.0.0.1:8080/;
set $true 1;
if ($true) {
# nothing?
return 400;
}
}
コレはオフィシャルの記事が間違っていた。
オフィシャルはif文の中で何もしていなかった。
この場合はちゃんとproxy_passが働いていた。
問題?なのはif文の中で返却ページに関する処理を行うときだ。
if文の中でreturnやrewriteなどを使用するとif文内の処理が優先されproxy_passは無視されることになる。
try_filesの無視
location /if-try-files {
try_files /2.html /1.html;
set $true 1;
if ($true) {
# nothing
}
}
こちらはなぜかif文の中で何もしなくてもifがあるだけでtry_filesが働かなくなる。
ここまで読んでいれば理解できると思うがもちろんif文の中にif文の中で返却ページに関する処理を書けば実行される。
ちなみにadd_header等は無視される。
proxy_passとの挙動の違いはなぜ。。。
SIGSEGV
location /crash {
set $true 1;
if ($true) {
fastcgi_pass 127.0.0.1:9000;
}
if ($true) {
# no handler here
}
}
SIGSEGVって書いてあるけどnginxはクラッシュしなかった。
がしかし、エラーは起きる。fastcgi_passされることもなくnot foundが返るでもない。。。
無論、if文内にreturnやrewriteを記述すればそちらが実行される。
if-and-alias
location ~* ^/if-and-alias/(?.*) {
alias /usr/local/opt/openresty/nginx/html/tmp/$file;
set $true 1;
if ($true) {
# nothing
}
}
この問題は指摘されているが特に何も問題なさそうだった。
最後になんでこの問題を修正しないのかと言う問いに対する答えが書かれていた。以下。
ifは強制的に評価して書き換えるもの、一方nginxの設定は宣言型。
ユーザーの要望によりif内で一部の非書き換えディレクティブを有効にしてしまったことが問題で、今に至る。。。
唯一の正しい修正はif内の非書き換えディレクティブをなくすことであるが、多くの設定が壊れるのでまだ完了してない。
それでもlocationディレクティブ内でifを使いたいなら
- ifの挙動を理解してね(ココにヒントがあるかも)
- 適切なテストしてね
だってさ
以上でnginxのevilなifの調査を終わります!
便利な機能なのでうまく理解して使いましょう。