nginxのtry_filesわかりにくすぎ問題
はじめに
後輩にnginxの設定がうまくいかないと相談され、「しょうがないなァ」と乗り出したのはいいものの、よくわからず後輩と一緒に一時間ぐらいウンウン唸っていたので備忘録として。
前提
該当のサイトではWebサーバとしてnginxを使用し、PHPファイルについてはphp-fpmへ処理を渡します。
また、PHPフレームワークを使用しており、実体ファイルの存在しないURIが指定された場合はDocumentRoot直下のindex.phpに処理が行くようにしたいです。
location / { index index.php index.html index.htm; try_files $uri $uri/index.html $uri/index.htm $uri/index.php /index.php?$args; } location ~ \.php$ { fastcgi_index index.php; ... }
確かこんな感じ。
この設定を反映させると、ドメインルート(https://example.com/)にアクセスされた際、PHPファイルがダウンロードされるようになってしまいました。
そもそもtry_filesとは
try_filesは引数を複数とります。左から順に実体ファイルが存在しているかどうかを探し、あればそのままそのファイルを参照してくれる、イカしたディレクティブです。
他にも、
try_files $uri $uri =404;
みたいにしてやると、ファイルが存在しない場合に404ステータスコードを返すこともできます。イカす。
try_filesの引数は最後のみ意味が違う
マニュアルも読まずに雰囲気で使っていると、「なるほど、try_filesは引数の左から順にファイルが存在するかを確認するディレクティブなんだな」という覚え方をしがちですが、厳密にはそうではありません。公式のマニュアルを引用します。
Syntax: try_files file ... uri; try_files file ... =code;
先程も言ったように、try_filesは複数かつ可変長の引数file
を取りますが、最後の引数はuri
もしくはcode
という別の扱いになっています。code
というのは先程説明した、ファイルが存在しない場合に指定のステータスコードを返すための処理です。今回はもう一つの
try_files file ... uri;
という用法について詳しく見てみます。
The path to a file is constructed from the file parameter according to the root and alias directives. It is possible to check directory’s existence by specifying a slash at the end of a name, e.g. “$uri/”. If none of the files were found, an internal redirect to the uri specified in the last parameter is made.
ファイルへのパスはfile、ルートおよびエイリアスディレクティブに従ってパラメーターから構築され ます 。名前の最後にスラッシュを指定することにより、ディレクトリの存在を確認することができます(例:「$uri/」)。ファイルが見つからなかった場合uri、最後のパラメーターで指定されたものへの内部リダイレクト が行われます。
(Google翻訳)
存在するファイルを探すのはfile
引数のみ、uri
引数についてはファイルを探したりせず、file
引数に該当するファイルがひとつも存在しなかった場合に問答無用で内部リダイレクトされるわけです。
file
引数はファイルへのアクセス、uri
引数は内部リダイレクト
マッチ後の挙動もfile
とuri
で異なります。
file
は実体ファイルのパスを指定するだけなのでphp-fpmに処理を渡すことができません。(やりようはあるのかもしれませんが…)
"前提"で取り上げた例では、$uri/index.php
に実体ファイルがあったのですが、php-fpmに渡されないまま実体ファイルにアクセスされ、PHPファイルがそのままダウンロードされるという現象が発生しました。
uri
はそれと違って内部リダイレクトが発生するため、locationディレクティブの再評価がされるようです。"前提"の例でいうと、
location / { index index.php index.html index.htm; try_files $uri $uri/index.html $uri/index.htm /index.php?$args; } location ~ \.php$ { fastcgi_index index.php; ... }
のようにしてあげると、ドメインルートにアクセスされた際にtry_filesディレクティブのfile
引数にマッチするものがなく、uri
引数である/index.php
に内部リダイレクトして再評価が行われ、\.php$
を指定したlocationディレクティブからphp-fpmに処理が渡される、というわけです。
おわりに
雰囲気でnginxをやっている勢はわりと躓きやすいんじゃないかと思っています。