Nginx + PHP-FPMをPATH_INFOがきちんと使えるように設定する

はじめに

NginxでPHP-FPMを使う場合、多くの解説サイトでは

location ~ \.php$ {
  ...
}

と設定すると書かれています。Nginxのデフォルトの設定ファイルにもコメントでこの書き方が載っています。

しかしこの設定では、PHPでPATH_INFOを取得することができません。より正確に言えば、PATH_INFOを含むURLはPHPとして実行されません。
つまり、http://example.com/foo.php/bar/というURLは前述の設定では扱えません。

このようなURLを使わないのならば問題ないのですが、多くのフレームワークではPATH_INFOを使いますし、mod_phpで出来る事が出来ないのは許せませんよね。

NginxのWikiの設定例

http://wiki.nginx.org/PHPFcgiExample にPATH_INFOを扱える設定例が載っています。

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

この方法でPATH_INFOは扱えることには扱えるのですが、うまく動かない場合もあります。 以下のような場合です。

問題が生じる例

上記の設定例では、サーバ上のディレクトリ名に.phpが含まれる場合正常に動作しません。

例えばhttp://example.com/foo.php/bar.php/foobar.phpというURLの場合、foo.phpPHPファイルである場合は動きますが、ディレクトリ名であった場合は動きません。

PATH_INFOをきちんと扱える設定

どのようなURLでもきちんとPATH_INFOを扱うには、php.inicgi.fix_pathinfo=0とする必要があります。 従って、PHP 5.3.8以前ではこの設定を使うとセキュリティ上問題が発生するので、PHP 5.3.9以降を使えない方はNginxのWikiに記載されている設定を使いましょう。

次に示す設定なら大体のURLでPATH_INFOがうまく扱えるはずです。

location ~ [^/]\.php$ {
  try_files $uri =404;
  fastcgi_split_path_info ^(.+\.php)(/.*)$;
  fastcgi_pass unix:/var/run/php5-fpm.sock;
  include fastcgi_params;
  fastcgi_param PATH_INFO $fastcgi_path_info;
}

location ~ [^/]\.php/ {
  try_files $uri $uri/ @php-fpm;
}

location @php-fpm {
  fastcgi_split_path_info ^(.+\.php)(/.*)$;
  fastcgi_pass unix:/var/run/php5-fpm.sock;
  include fastcgi_params;
  fastcgi_param PATH_INFO $fastcgi_path_info;
}

もっといい書き方があったら教えてください。