nginx の php-fpm で index.php のフロントコントローラーありきの設定

おそらく素の php ファイルをドキュメントルートに置いて URL に拡張子 .php を含むアクセスを有効にするため、location で正規表現とか、try_files の最後で index.php とか、fastcgi_split_path_info とか、いろいろ難しいことがされている例を良く見るのですが、今日日の php ならドキュメントルートには index.php だけでフロントコントローラーありきだろうので、それならもっとシンプルにできるのでは、というメモ。

最もシンプルにはこれだけで良さそうです。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;

    # ファイルが存在しなければ @app へ内部リダイレクト
    try_files $uri @app;

    location @app {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;

        # すべて index.php へ
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }
}

これだと http://example.com/index.php で素の PHP ファイルが見えてしまいます。開発環境なら別に構いませんが本番環境でそれでは困るので /index.phpreturn 404 すれば良いでしょうか。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;
    try_files $uri @app;

    location @app {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }

    location = /index.php {
        # /index.php に 404 を返す
        return 404;
    }
}

あるいは index.php をドキュメントルート外に移しても良いですね。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;
    try_files $uri @app;

    location @app {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;

        # ドキュメントルート外のファイルを指定
        fastcgi_param   SCRIPT_FILENAME /app/index.php;
    }
}

ディレクトリアクセスで index.html が見えない

なお、これだとサブディレクトリに index.html があるとき、その index.html を省略してディレクトリの URL だけでアクセスできません。

それで困ることもあまりないように思いますが、なにかの事情で静的なページを混在させるときには困ることもあるかもしれないので、次のように try_files で index.html も存在チェックさせると良いでしょうか。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;

    # ディレクトリの index.html も存在チェックする
    try_files $uri $uri/index.html @app;

    location @app {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }

    location = /index.php {
        return 404;
    }
}

これだと http://example.com/sub のように末尾に / が無いときまで index.html を返してしまいます。なんとなく気持ち悪いし、相対パスがリンク切れになってしまうので、次のように index を指定したうえで try_files でディレクトリを存在チェックさせるとよいでしょうか。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;

    # インデックスのファイル名を指定
    index index.html;

    # ディレクトリを存在チェック
    try_files $uri $uri/ @app;

    location @app {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }

    location = /index.php {
        return 404;
    }

    # / を fastcgi へ
    location = / {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }
}

これなら末尾に / がないときは / が付与された URL へのリダイレクトになります。

なお http://example.com/ のような URL までディレクトリと解釈されないように location = / で fastcgi に向かわせる必要があります。

2つの location の内容がすごくかぶってるので indextry_filesindex.php を指定する方が良いかも。

server {
    listen 80 default;
    charset utf-8;

    root /app/public;

    # インデックスに index.php も追加
    index index.html index.php;

    # ファイルやディレクトリが存在しないときは /index.php へクエリストリング付きで内部リダイレクト
    try_files $uri $uri/ /index.php$is_args$args;

    location = /index.php {
        fastcgi_pass    unix:/sock/php-fpm.sock;
        include         fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
    }
}

さいごに

結局良く見る設定とあまり変わっていない気もする・・

なお、上記の設定だとドキュメントルート内に index.php 以外の php ファイルを置くと生の内容が見えてしまうので注意。