All Articles

nginxのフロントコントローラフレームワーク用rewrite設定メモ

多分汎用的に使えそうなものだったので備忘録として残しておきます。

nginxでApacheのmod_rewriteと同じようなことをする

nginxにはデフォルトでrewriteモジュールがバンドルされていて、そのままrewirteの設定が記述できますが、フロントコントローラタイプのフレームワークを使う時に、アクセスをindex.phpに集約し、かつindex.phpのURI表記を消すようなrewrite設定にちょっと手間取ってしまいました。(なかなか自由度が高く、それ故に上手く行きませんでした)そのメモです。今回はseezoo、ひいてはCodeIgniter+nginxのような設定ですが、おそらく他のフロントコントローラタイプのFWにも使えそうです。

locationディレクティブの設定

今回ここでかなりつまづきました。プレフィックスの記述方法で評価の順番が変わったり、rewriteをかけたりすると無限ループに陥ったり。。。(;´Д`)色々試行錯誤して、以下のような感じで動作させることができました。serverディレクティブのみ抜粋しています。

server {
        listen 80;
        server_name .xxxx.xxx;
        server_tokens Off;
        access_log /var/log/nginx/xxxx/access_log;
        error_log /var/log/nginx/xxxxx/error_log;
        index index.html index.php;
        root /var/www/html;

        # expires flag
        set $expire_enable 0;

        # systemディレクトリは直接アクセスさせない
        location ^~ /system {
                rewrite ^(.*)$ /index.php$1 last;
        }

        # ルートアクセス設定
        location / {
               root /var/www/html;
        
               if (!-f $request_filename) {
                        rewrite ^(.+)$ /index.php$1 last;
                }
        }

        location ~ .*\.php {
                set $path_info "";
                if ($uri ~ "^/index\.php([^\?]*)") {
                        set $path_info $1;
                }
                fastcgi_buffers 16 16k;
                fastcgi_buffer_size 32k;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME var/www/html/index.php;
                fastcgi_param PATH_INFO $path_info;
                include fastcgi_params;
                fastcgi_pass 127.0.0.1:9000;
        }

        location ~ /\.ht {
                deny all;
        }

        location ~ .+\.(jpg|jpeg|gif|css|png|js|ico|swf|pdf)$ {
                if ($expire_enable = 1) {
                        expires 1d;
                }
        }
}

PHPはphp-fpmで9000番ポートをlistenしています。 ちなみに、nginxのサイトにもCodeIgniter用のrewirte記述例があるのですが、これが上手く動かず・・・というか、system/以下のファイルパスを指定するとアクセスできちゃうようなんですよね(実際はCodeIgniterのphpファイル冒頭でexitしているので大丈夫だとは思いますが)。ちょっと気持ち悪いので、system/以下のファイルアクセスもPATH_INFOに渡すように設定しています。

ちなみに、これはCodeIgniter1.7のケースなので、2.x系なら、application/以下もrewrite対象に含めたほうがいいですね。 また、他のFWであれば、別途直接アクセスさせないディレクトリは追記しないといけないです。

PATH_INFOでルーティングしたい!

その他のサンプルサイトだと、index.php?$1などとして、ルーティングをREQUEST_URIに変更しましょうみたいな事が書いてありますが、私はPATH_INFOにどうしてもルーティングしたかったんです…。で、nginxのrewriteによるPATH_INFOは設定されないようなので、自前でPATH_INFOを生成してfastcgi_paramに渡しています。以下の部分です:

set $path_info "";
if ($uri ~ "^/index\.php([^\?]*)") {
  set $path_info $1;
}
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME var/www/html/index.php;
fastcgi_param PATH_INFO $path_info;

nginxは設定内で「set $xxx value」などとして変数を扱うことができるようで(すごいけど違和感)、$path_info変数を宣言し、rewirte後のURIからPATH_INFO部分を正規表現でマッチングしてパラメータとして渡すようにしています。これでPHP側では$_SERVER['PATH_INFO']で取得できるようになりました。この辺はやっぱりまだ Apacheの方がラクな感じですね。

一応_$GETも取れるように

fastcgi_buffers 16 16k; fastcgi_buffer_size 32k;という設定がありますが、rewriteでクエリストリングを渡すとバッファがオーバーフローする場合があるようで、この記述をするとクエリストリングもちゃんと渡るようになりました。

ともあれ

これでseezooもnginxで動作させることができるようになりました(∩´∀`)∩ 直接アクセスに対して設定を色々追記しないといけないので、この辺はまだApacheに軍配が上がる、といった感じでしょうか。ドキュメントルート内にコアファイルを置くような設計だと面倒かもしれません。コアファイル群をルート外に分離できるFWとなら相性がいいような印象でした。

色々試行錯誤していますが、nginxは触ってて楽しいので、引き続き勉強を続けていきたいと思います。 (実戦で導入するにはまだちょっと二の足を踏んでいますが…)