Laravel4をNginxで動かす

タグ: Laravel4   Nginx  

Nginxでちょっと複雑な構成を処理しようと思い、検索すると、やたら面倒な設定を行っているページが引っかかってしまいます。ただ、特定のサブディレクトリーにアクセスが来たら、それは別のドキュメントルートで動かしたいだけなのです。

ちょっとしたことをやりたいだけなのに、ここまで複雑なのかと諦めてしまいがちです。実際、一度今作っているサービスを諦めかけました。サイト自身の構成を何度も変更するはめになりました。しかし、Nginxが人気を得ているということは、そんなに複雑なはずはないと思い直しました。

シンプルにNginxだけをキーワードにし、出てきた結果を片っ端からチェックしていき、やっと基本的な情報が見つかりました。

つまり、複雑なやり方を紹介したページが検索に引っかかりやすくなっており、その上、基本的な情報が見つかりづらくなっています。結局、最終的には、公式ドキュメントから参考してリンクされている、英語のページがとても役立ちました。

では、Nginx上で動かしたい、Laravel初心者のために、基本的なパターンを紹介しましょう。内容はUbuntu14.04をサーバーとして立てる場合です。ディレクトリー構成は、デフォルトのままのOS/Linuxディストリビューションが多いようですので、参考になるでしょう。

大抵の場合、ホストごとに仮想ホストを設定します。defaultが最初から作成されています。今回はこれをそのまま利用します。まず、元から用意されているファイルのコメントを取り除いてみます。

ファイル:/etc/nginx/sites-enabled/default

server {
    listen 80;
    listen [:]:80 default_server ipv6only=on;

    root /usr/share/nginx/html;
    index index.html index.htm;

    server_name localhost;

    location / {
       try_files $uri $uri/ =404;
    }
}

以降のサンプルで利用する共通部分は、次のようになります。サーバーのドメイン名を指定してください。ローカルからアクセスする場合は、localhostも指定しておきましょう。

server {
    listen 80;
    # 使用するドメイン名
    server_name sample.example.com;

    # ここに以降のサンプルディレクティブを追加する
}

注意: Nginxの設定ファイルを変更した後は、内容を反映させるためにservice nginx force-reloadを実行しましょう。reloadでは反映されないことがあります。restartは多少時間を取るようです。

ドメインで直接Laravelを動作させる

一番単純なパターンです。ドメインでも、それを利用したサブドメインでも、それが指定された場合に、直接ドキュメントルートを呼び出すパターンです。単純なWebアプリではこれが一番多いでしょう。Laravelのドキュメントに記述されているディレクティブで動作します。

Laravelを/room/vagrant/laravel-dirにインストールした場合です。

        # ドキュメントのルートディレクトリーを
        # Laravelをインストールしたディレクトリーの
        # publicに指定する
        root /home/vagrant/laravel-dir/public;

        # try_filesで$uri/を有効にする場合は
        # 以下のindexも指定する
        # index index.html index.php;

        # 末尾のスラッシュの削除
        rewrite ^(.+)/$ $1;

        location /
        {
            # フォルダーへアクセス時にindex.htmlなどを表示したい場合は
            # 以下のように"$uri/"も指定する。
            # try_files $uri $uri/ /index.php?$query_string;
            try_files $uri /index.php?$query_string;
        }

        location ~ ^/index.php$ {
            include fastcgi_params;
            # SCRIPT_FILENAMEをオーバーライト
            fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_split_path_info ^(.+\.php)(.+)$;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
        }

location /は先頭が/で始まるURIで一致します。つまり、全リクエストに一致します。

try_filesは以降に指定した順番に存在をチェックし、存在すればそれが処理されます。$uriで指定されたURIがドキュメントルートで存在しているかを先ずチェックします。$uriには、ドメインに続いて指定されたURIの内容が入っています。一致すれば、それに対するアクセスとして処理されます。これにより例えば、CSSやJS、画像などのファイルにアクセスが通ります。

$uri/を指定に含めた場合、$uriをディレクトリーとして存在しているかチェックします。ディレクトリーとして存在していれば、indexディレクティブに指定されている順番に、そのディレクトリー中のファイルがチェックされます。今回はこの処理を行いません。指定してしまうとindexディレクティブで指定したファイルがない場合、Nginxのレベルで403エラーが返されてしまいます。エラーページをNginx側で表示したいのか、それともLaravel側で表示したいのかにより、指定を分けましょう。今回はLaravelにまかせ、統一した見かけのエラーページを表示するために指定していません。

最後は/index.phpです。index.phpがチェックされます。Laravelの場合、publicフォルダーにindex.phpが含まれていますので、これに一致します。後に付けた?$query_stringにより、URIに付けられたクエリー文字列をそのまま引き継いでいます。

このtry_filesディレクティブに一致すると、その内容でもう一度最初からlocaltionの条件が調べられます。もちろん、指定されたURIそのままを表す$uriにマッチした場合、一致するものが既に見つかっていますので、再度locationはチェックされません。

location ~ ^/index.php$で、ルートディレクトリー直下のindex.phpにマッチングさせています。多くの場合、この正規表現はlocation ~ \.php$という形で指定することが多いようです。ですがこの指定は、phpファイル全部を実行可能にしてしまいます。public下にindex.php意義のphpファイルを置くことは、通常ありませんのでどちらでもかまわないのですが、もし設置する必要がある場合、それらのファイルにアクセスさせたければ、\.phpの指定により、PHPファイル全部にアクセスさせましょう。逆にindex.phpだけをphpとして処理したい場合は、この例のように書けます。

このfastcgiへ渡すパラメーターを用意しているディレクティブでよく、include fastcgi_paramsを一番最後に指定してる例が多くあります。includeディレクティブは、指定されたファイルを読み込見ます。このファイルの中は、fastcgi_paramディレクティブの塊になっており、fastcgiの標準的な設定になっています。今回の例のように、一部を書き換える必要がある場合、includeの後で指定する必要があります。後に指定した内容が、先に指定した内容を上書きするからです。

今回、URIの最後がスラッシュで終わる場合、つまりディレクトリーを示すURIの場合、まずrewriteにより、末尾のスラッシュが取り除かれます。その後、location /に一致し、try_filesの最初の指定でファイルとして存在をチェックし、見つからない場合index.phpに渡され、Laravelのルーティングで処理が分けられます。

もし、URIの最後のスラッシュを取り除くrewriteを取り外せば、最初のlocation /に一致し、$uriでディレクトリーとしてチェックします。そのディレクトリーが存在していれば、indexで指定されたファイルを順番にチェックし、一致するものが存在すれば、それが送り返されます。なければ最後に指定したアイテムが適用されます。その一致した結果も内部的にリライトされ、再度locationの評価を受けますが、今度は最後のindex.phpのlocationに一致しますので、fastcgiとして処理されます。

これがシンプルなバージョンです。シンプルですが、多少問題もあります。CSSやJSや画像を指定し間違えた場合も、index.phpへ渡ってしまうため、Laravel本体で処理され、オーバーヘッドがかかってしまいます。とは言え、開発中ではなく本番環境では、そのような事態は起きないでしょうから、大きな問題ではありません。

そのようなリソースへのアクセスが404になる可能性があるならば、以下のディレクティブを追加しましょう。

location ~ ^/(css|js|fonts|img)/ {
    try_files $url /404.html
}

publicディレクトリーの中に、リソースアクセス失敗用のシンプルな404ページを用意しておき、それをレスポンスとして返させます。css|js...というのはpublicフォルダーの中に存在するフォルダー名です。そうしたリソース用のフォルダーの中へアクセスされた場合は、try_filesでそれが存在するか調べ、存在しない場合は404.htmlを表示しています。(前に試したものを記憶を思い出し書いています。間違っているかも知れません。)

特定のサブディレクトリーで動作させる

サブディレクトリーとしてLaravelを動作させましょう。PHPフレームワークをサブディレクトリーで動かそうとして、検索した情報を参考にすると、結構ハマります。日本語情報の場合、必要以上に複雑な方法がヒットするようです。英語情報もかなりヒットしますが、どういう条件で動作させるのかが明示されていないものもありますので、むやみにコピーし多少修正してもうまくいきません。

今回は基本となるシステムがルートのURIで動作しており、/blog配下のURIが指定された場合のみ、Laravelが動作するものと考えてみましょう。ルートはPHP製のCMSかフレームワークで動作し、Laravelと同様にpublicディレクトリー配下のindex.phpが処理の入り口だとしましょう。

ディレクトリー構造は以下のように仮定しましょう。よくあるパターンです。

/home/vagrant/root-system/
             |           +public/
             |                  +index.php
             |                  +css/
             |                  +js/
             |                  +img/
             +blog-system/
                         +public/
                                +index.php
                                +css/
                                +js/
                                +img/

ルート用の定義に、ブログ用の定義を追加します。

    # 末尾のスラッシュの処理
    rewrite ^(.+)/$ $1;

    # ルートのシステムの定義
    location /
    {
        try_files $uri /index.php?$query_string;
    }

    location ~ ^/index.php$ {
        include fastcgi_params;
        # SCRIPT_FILENAMEをオーバーライト
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
    }

    # /blogでアクセスできる、サブディレクトリーのブログシステムの定義
    location ~ ^/blog(/(.+))?$ {
        root /home/vagrant/blog-system/public;

        try_files $1 /blog/index.php?$query_string;

        location ~ ^/blog/index.php$ {
            include fastcgi_params;
            # パラメーターをオーバーライト
            fastcgi_param SCRIPT_FILENAME /home/vagrant/blog-system/public/index.php;
            fastcgi_split_path_info ^(.+\.php)(.+)$;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
        }
    }

さんざん試して、できたのがこれです。index.phpまで届かせるのは簡単に行きましたが、LaravelのルーティングやURLの生成がうまく行かず、いろいろ足し、成功してからいろいろ引いて、残ったのがこれです。

ポイントになるのは、index.phpはブログのpublic/index.phpに決め打ちですので、直接指定していることでしょうか。汎用的に書くなら$document_rootなどを利用するのでしょうが、あとから見た時にひと目でやりたいことがわかるので、直接書きました。

fastcgi_passで指定している、ソケットファイルを変更することで、本体のPHP設定と、サブディレクトリーで起動されるフレームワークのPHP設定を分けることもできます。PHP-FPMでのPHP設定は、/etc/php5/fpm/php.iniがベースとなりますが、/etc/php5/fpm/pool.d下にあるPHP-FPMの設定ファイルを個別に記述でき、php.iniの設定値を変更できます。(一部の設定値は上書きではなく、追加されます。詳しくは日本語になっていますので、PHP-FPMのドキュメントをチェックしてください。)前記事で紹介したInstantLaravelでも使用していますので、参考にしてください。

ヒント

思い通りに動かない場合は、以下の点をチェックしましょう。

  1. locationの評価順序
  2. 内部リライトが起きると、その結果に対して再度locationが評価されることを思い出す
  3. 内部リライトは、returnとrewrite、indexとtry_filesディレクティブで起きることを理解する
  4. エラーが起きたときは、原因を特定するため、ログファイルを調べる。
  5. デバッグ用のログのとり方を覚える。
  6. ファイルのパーミッションを再度チェックする。

1と2は、他のWebサーバーやコンピュータ言語でも同じような苦労が入ります。

3も認識すれば、済むだけです。

4.は常套手段です。上手く行かない場合、指定だけを見返しても原因の特定は難しいものです。ログの内容が解決の糸口になってくれることが多いものです。

5はいざという場合に、役に立ちます。通常のエラーログは以下のように取ります。

error_log ログファイル名 error;

リライトのログを取りたい場合は、以下のように変更します。

error_log ログファイル名 notice;
rewrite_log on;

locationの評価も調べたい場合は、ログレベルをdebugにします。

error_log ログファイル debug;
rewrite_log on;

表示される情報が多くなり、ヘッダーなどlocation以外の情報も表示されます。