PHP FastCGI の例

この例は、内蔵されたPHP FPM (FastCGI プロセスマネージャー)を使った新しいPHP (>= 5.3.3) のためのものです。

このガイドは、PHP FPMが既にインストールされていて、tcpポート (127.0.0.1:9000) あるいはunixソケット (/var/run/php-fpm.sock) のどちらかを使うように設定されていることを仮定します。

PHP FPMを使ったNGINXの設定についての多くのガイドがありますが、それらの多くは不完全 (PATH_INFO を正しく扱っていない) か、セキュリティの問題(スクリプトが確かなphpファイルかどうかをチェックしない)を含んでいます。

FastCGI パラメータ

まず、全ての代表的なFCGI設定を1つのファイルにまとめ、それらをimportすることをお勧めします。

例えば、debinaおよびubuntu上でデフォルトで以下のような /etc/nginx/fastcgi_params ファイルがあります:

fastcgi_param   QUERY_STRING            $query_string;
fastcgi_param   REQUEST_METHOD          $request_method;
fastcgi_param   CONTENT_TYPE            $content_type;
fastcgi_param   CONTENT_LENGTH          $content_length;

fastcgi_param   SCRIPT_FILENAME         $document_root$fastcgi_script_name;
fastcgi_param   SCRIPT_NAME             $fastcgi_script_name;
fastcgi_param   PATH_INFO               $fastcgi_path_info;
fastcgi_param       PATH_TRANSLATED         $document_root$fastcgi_path_info;
fastcgi_param   REQUEST_URI             $request_uri;
fastcgi_param   DOCUMENT_URI            $document_uri;
fastcgi_param   DOCUMENT_ROOT           $document_root;
fastcgi_param   SERVER_PROTOCOL         $server_protocol;

fastcgi_param   GATEWAY_INTERFACE       CGI/1.1;
fastcgi_param   SERVER_SOFTWARE         nginx/$nginx_version;

fastcgi_param   REMOTE_ADDR             $remote_addr;
fastcgi_param   REMOTE_PORT             $remote_port;
fastcgi_param   SERVER_ADDR             $server_addr;
fastcgi_param   SERVER_PORT             $server_port;
fastcgi_param   SERVER_NAME             $server_name;

fastcgi_param   HTTPS                   $https;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param   REDIRECT_STATUS         200;

Ubuntu Precise (12.04) を使っている場合、SCRIPT_FILENAME を変更し、PATH_INFOパラメータを追加することに注意してください。

NGINXをPHP FPMに接続

ここで、NGINXがFCGIプロトコルを使ってリクエストをPHP FPMにproxyするように指示しなければなりません:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi_params;
}

unixソケットを使っている場合は、fastcgi_pass を以下に変更してください:

fastcgi_pass unix:/var/run/php5-fpm.sock;

NGINXを再起動します。

テスト

ちょうど以下のものを含むtest.phpをNGINXドキュメントルート上に作成します:

 var_export($_SERVER)?>

ブラウザ内で以下をリクエストしてみてください: # /test.php # /test.php/ # /test.php/foo # /test.php/foo/bar.php # /test.php/foo/bar.php?v=1

REQUEST_URI, SCRIPT_NAME, PATH_INFO および PHP_SELF の値に注意してください。

以下はhttp://lemp.test/test.php/foo/bar.php?v=1の正しい出力です

array (
  'USER' => 'www-data',
  'HOME' => '/var/www',
  'FCGI_ROLE' => 'RESPONDER',
  'QUERY_STRING' => 'v=1',
  'REQUEST_METHOD' => 'GET',
  'CONTENT_TYPE' => '',
  'CONTENT_LENGTH' => '',
  'SCRIPT_FILENAME' => '/var/www/test.php',
  'SCRIPT_NAME' => '/test.php',
  'PATH_INFO' => '/foo/bar.php',
  'REQUEST_URI' => '/test.php/foo/bar.php?v=1',
  'DOCUMENT_URI' => '/test.php/foo/bar.php',
  'DOCUMENT_ROOT' => '/var/www',
  'SERVER_PROTOCOL' => 'HTTP/1.1',
  'GATEWAY_INTERFACE' => 'CGI/1.1',
  'SERVER_SOFTWARE' => 'nginx/1.4.0',
  'REMOTE_ADDR' => '192.168.56.1',
  'REMOTE_PORT' => '44644',
  'SERVER_ADDR' => '192.168.56.3',
  'SERVER_PORT' => '80',
  'SERVER_NAME' => '',
  'HTTPS' => '',
  'REDIRECT_STATUS' => '200',
  'HTTP_HOST' => 'lemp.test',
  'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0',
  'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.5',
  'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
  'HTTP_CONNECTION' => 'keep-alive',
  'PHP_SELF' => '/test.php/foo/bar.php',
  'REQUEST_TIME' => 1367829847,
)

注意

  1. The location regex capable to handle PATH_INFO and properly check that the extension indeed .php (not .phps) whether there is PATH_INFO or not.
  2. The fastcgi_split_path_info regex capable to correctly handle request like /test.php/foo/blah.php or /test.php/.
  3. The if lets NGINX check whether the *.php does indeed exist to prevent NGINX to feeding PHP FPM non php script file (like uploaded image).

Some guides recommend to use try_files instead of if, if you do that, beware of NGINX bug #321. I personally think if is more appropriate for this, even If Is Evil agree this is one of the 100% safe thing to use if with.

このガイドはphp.ini 上のcgi.fix_pathinfo = 1 (デフォルト)で良く動作します。幾つかのガイドはそれをcgi.fix_pathinfo = 0 に変更するように言い張りますが、そうするとPHP_SELF 変数を破壊します(DOCUMENT_URIと同じではありません)。