Nginxのalias設定でFile not foundになる

Nginxでaliasを設定したのですが少し手間取りました。どうもPHPをNginxで動かすのは案外ハマりどころが多い気がしています。

やりたかった事

http://example.com/wordpress/wp-content/themes/sample-theme/game/index.php

上記のPHPファイルを下記のURLで実行できるようにします。

http://example.com/game/index.php

今回のようなPHPファイルの配置はたとえばWordPressの中に簡単なゲームとか占いなどの素のPHPを置きたい場合などに使えますね。

「File not found.」が出る

まず、こんな感じの設定でaliasを試してみました。

/etc/nginx/conf.d/default.conf

server {
  
  listen       80;
  server_name  _;

  fastcgi_read_timeout 300;
  client_max_body_size 10m;

  root   /share/wordpress;
  index  index.php index.html;

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }

  location / {
    try_files $uri $uri?$args $uri/ /index.php?$uri&$args /index.php?$args;

  }

  location /game {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
  }

  location ~ \.php$ {
    fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME $request_filename;
    include         fastcgi_params;
  }

}

Nginxを再起動してhttp://localhost/game/にアクセスするとFile not found.になります。ただ、gameフォルダ以下に配置されたtest.html等の静的ファイルは表示される状態になっています。

アクセスが.phpの場合のスクリプト用の設定をしてあげる必要があったので試してみます。

/etc/nginx/conf.d/default.conf

  
  location /game {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
  }
  location ~ ^/game/.*\.php$ {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
    fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME $request_filename;
    include         fastcgi_params;
  }

結果は変わらずFile not found.です。$request_filenameを$fastcgi_script_nameに変えても同様です。そもそもこの変数の中身何が入ってるのか理解があやふやだったので調べてみます。

Nginxの変数をログ出力する

Nginxの変数のログ出力についてはこちらにまとまっていました。とても参考になりました。

http://ytsuda.hateblo.jp/entry/2013/06/13/212749

やり方はリンク先そのままですが、下記のようになります。

/etc/nginx/nginx.conf

http{
  log_format debug_val_format "$debug_val";
  ...
}

/etc/nginx/conf.d/default.conf


  location ~ ^/game/.*\.php$ {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
    fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME $request_filename;
    include         fastcgi_params;

    set $debug_val "request_file: $request_filename, fastcgi_script_name: $fastcgi_script_name";
    access_log /var/log/nginx/debug_val.log debug_val_format;
  }

Nginxを再起動してhttp://localhost/game/にアクセスすると下記のようにログが出ました。

/var/log/nginx/debug_val.log

request_file: /share/wordpress/wp-content/themes/sample-theme/game/, fastcgi_script_name: /game/index.php

$request_filenameはaliasで設定したディレクトリまでのパスが、$fastcgi_script_nameには実行対象のスクリプトのパスが入っています。fastcgi_param SCRIPT_FILENAMEに渡したいのは /share/wordpress/wp-content/themes/sample-theme/game/index.php なので、$request_file$fastcgi_script_nameを渡しても /share/wordpress/wp-content/themes/sample-theme/game/game/index.php となって同様にうまくいかないので $fastcgi_script_name の値を加工してやる必要があります。

$fastcgi_script_name の最後のスクリプト名だけ取得する

簡単に正規表現を使ってできないのかなーと思っていたんですが、mapモジュールというものを使って実現するようです。このmapモジュールはある変数から新しい変数を切り出すというもので、下記では$fastcgi_basenameという新しい変数を$fastcgi_script_nameから切り出しています。

/etc/nginx/nginx.conf

http {
  ...
  map $fastcgi_script_name $fastcgi_basename {
    ~/(?<captured_request_basename>[^/?]*)(?:\?|$) $captured_request_basename;
  }
  ...
}

これで$fastcgi_basenameという変数が利用できるようになりました。

最終的に動いた設定

/etc/nginx/nginx.conf

http {
  ...
  map $fastcgi_script_name $fastcgi_basename {
    ~/(?<captured_request_basename>[^/?]*)(?:\?|$) $captured_request_basename;
  }
  ...
}

/etc/nginx/conf.d/default.conf

server {
  
  listen       80;
  server_name  _;

  fastcgi_read_timeout 300;
  client_max_body_size 10m;

  root   /share/wordpress;
  index  index.php index.html;

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }

  location / {
    try_files $uri $uri?$args $uri/ /index.php?$uri&$args /index.php?$args;

  }

  location /game {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
  }
  location ~ ^/game/.*\.php$ {
    alias /share/wordpress/wp-content/themes/sample-theme/game/;
    index index.php;
    fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME $request_filename$fastcgi_basename;
    include         fastcgi_params;
  }

  location ~ \.php$ {
    fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index   index.php;
    fastcgi_param   SCRIPT_FILENAME $request_filename;
    include         fastcgi_params;
  }

}

上記の設定でうまく動作しました。

ちなみに「location /game」と「location ~ ^/game/.*.php$」の2つディレクティブの定義が必要でやや冗長なのですが、これをたとえば「location ~ ^/game/」というようにまとめて1つのディレクティブで定義しようとするとスクリプトは動くのですが、今度はHTML等の静的ファイルが「Access denied.」というエラーになります。

あらためてNginxの設定は冗長…

Apacheと同じ感覚でNginxを使い始めて数ヶ月なんですが、たとえばlocationの優先順位など注意しないといけない事が多い気がしています。設定も綺麗に切り出せないし。ApacheとNginxの設定の概念の違いはこちらに分かりやすくまとまっていました。Apacheとの違いは意識して使っていかないと大ハマリしますね(過去にやった)。