nginxのX-Accel-Redirectを試してみる
Meity に実装した写真アップロード機能で、画像を表示する際に Apache の XSendfile ではなく nginx の X-Accel-Redirect を使うようにしたのでその作業メモ。
画像ファイルを表示するための URL にアクセスされたら、まず PHP 側で認証等をチェックしてアクセス権限があるかどうかを調べ、問題なければ X-Accel-Redirect ヘッダーに画像ファイルへのパスを指定して返す。
例えば実際の画像ファイルがサーバー上の「/var/www/secret/pics」というディレクトリ内にある場合、
location ^~ /picpath/ { alias /var/www/secret/pics; internal; }
のように nginx で設定したとすると、X-Accel-Redirect ヘッダーには次のように「/picpath」で始まるパスをセットする。
header('X-Accel-Redirect: /picpath/picfile.jpg');
これで内部的には「/var/www/secret/pics/picfile.jpg」が読み込まれて返される。つまり「/picpath」の部分はアプリケーションと nginx との間だけで使われるパスなので何でもよいが、サイト内で公開されている URL と一致しないものを選ぶこと。
なお、公式ドキュメントにあるように alias ではなく root を使うことも可能。例えば、
location ^~ /pics/ { root /var/www/secret; internal; }
と nginx に設定した場合、先程と同様に「/var/www/secret/pics/picfile.jpg」のファイルを返したければ、今度は location が /pics/ で始まらなければならないので、
header('X-Accel-Redirect: /pics/picfile.jpg');
とする。この場合「/var/www/secret」のあとにそのまま「/pics/…」のパスが連結される。alias と root のどちらを使うかはディレクトリ名が URL とかぶらないかどうか等で決めればといいと思う。
ハマったポイントは location ディレクティブの優先順位。最初は以下のように location の後ろに修飾子をつけずに /picpath/ を指定していたのだが、X-Accel-Redirect に指定したファイルが画像ファイルだったため、その下にある正規表現で指定されたディレクティブにマッチしてしまっていた。
location /picpath/ { alias /var/www/secret/pics; internal; } # こっちの優先順位が高かった location ~ .*\.(jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO)(\?.+)?$ { root /var/www/somehost/public_html; index index.html; break; }
location の後に修飾子を置かないものは前方一致になるが、正規表現より優先順位が低い。なので正規表現より優先順位の高い「^~」をつけなければならなかった。(参考: Nginx で location の判定方法と優先順位を調べる | レンタルサーバー・自宅サーバー設定・構築のヒント)
location ^~ /picpath/ { alias /var/www/secret/pics; internal; }
優先順位に問題がなければ「^~」を使う必要はない。
あと location のマッチングが期待通りに動作しているか調べるために開発環境ではエラーログに debug を指定しておくといい。
error_log /var/log/nginx/error.log debug;
X-Accel-Expires を使えばキャッシュが効くのだろうかと試してみたが、いまいち上手くいってない模様。これは調査中。
コメントを残す