nginx rewrite おさえるべきポイント

こんにちは角田(tsunoda)です。

今回はapacheからnginxへの移行の際のrewriteについてです。
最近さわっていて結構ハマったので、おさえるべきポイントをご紹介します。
自分も調べながらやったのですが、お困りの方が多いように思えました。
nginxへ移行をお考えの方、基本的な事ですがご参考にして頂ければ幸いです。

nginx rewrite

nginxでは、
設定ファイルにrewrite文を書く事で、
URLをリダイレクトすることができます。

apacheの.htaccessが使えません(^o^)/
.htaccessの中身はすべてnginxの設定ファイルに変換して設定する必要があります。

自動的に変換してくれるサイトもありますが、
鵜呑みにすることなかれ。。ざわざわ…

それでは押さえるべきポイントをご紹介します。

location の優先順位

locationではURLに応じた様々な制御を行うことができます。
locationの優先順位は以下のようになっています。

1. =    : 完全一致  
2. ^~   : 前方検索(正規表現より優先度が上)  
3. ~    : 正規表現(大文字小文字区別あり)  
4. ~*   : 正規表現(大文字小文字区別なし)  
5. なし : 前方検索(正規表現より優先度が下)  

以下で解説。


例えば次のような要求がきた場合、こんな判定になります。

要求 : http://example.com/ → configuration A
要求 : http://example.com/index.html → configuration B
要求 : http://example.com/documents/document.html → configuration C
要求 : http://example.com/images/1.gif → configuration D
要求 : http://example.com/documents/1.jpg → configuration E

自分はここら辺からすでに魔術にかけられていたようです。。

この優先順位によって、狙ったlocationが別のlocationで捕まっていたりするので要注意。
rewriteが効いてない場合なんかはlocationの優先順位を見ることが解決の鍵になるかも。
あまり大きな正規表現は使わない方が良いですね。

apacheでは見れているのにnginxでは見れない404だ!

nginxでphpがダウンロードされちゃう!

リダイレクト先のlocationを作って、
そこに直接fastcgiの設定を入れてあげます。
この設定が効いていない事が多いです。

(例
location = /sample/ {
  rewrite ^ /hogehoge/sample/ permanent;
}
#リダイレクト先の設定↓
location = /hogehoge/sample/ {
  try_files $uri /hogehoge/sample/index.html;
  include       /path/to/nginx/fastcgi_params;
  fastcgi_path  unix:/path/sample.sock;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  fastcgi_param PATH_INFO $fastcgi_path_info;
  fastcgi_param APP_ENV develop;
}

locationごとに毎回設定を書くのは面倒なので、
fastcgiの設定ファイルを別で作り(fastcgi-php.txt.txtとか)、
それをincludeする形にするとさっぱりしますね。

location = /hogehoge/sample/ {
  try_files $uri /hogehoge/sample/index.html;
  include /path/to/nginx/fastcgi-php.txt.txt
}

try_files

Nginxの設定にて、静的なファイルと動的なURLを振り分けたいことがあります。
try_filesを利用すれば、指定した順番で確認と転送を実施してくれます。

以下の順番で確認してくれます。

  1. URLのパスにファイルがあるか($uri)
  2. 1がなかった場合、URLのパスにディレクトリがあるか($uri/)
  3. 1も2もなかった場合、指定したロケーションへ行く。(/hogehoge/sample)
location /sample {
  try_files $uri $uri/ /hogehoge/sample;
}

リダイレクトする前のURLのまま、リダイレクト先のphpなどを実行したい場合に使えます。

URLはhttps://sample.jp/sampleのまま、
https://sample.jp/hogehoge/sample/index.phpを読み込ませたい。
↓
location = /sample/ {
   try_files $uri $uri/ /hogehoge/sample/$args;
}

こんな感じで。

こうすればブラウザのアドレスバーはhttps://sample.jp/sample のまま、
内部でリダイレクトされて、
https://sample.jp/hogehoge/sample/index.php が適用されます。

内部リダイレクトを実行した場合、
すべてサーバー内部で処理されるため、表にその痕跡は一切現れず、
クライアント側からは書き換えが行われたことを知ることは出来ません。

「あれー、try_filesしてるのに内部リダイレクトされないな。。try_filesが効かない..」
そう思い、try_filesの記述を書き直したりして見事に自分はハマりました。。

try_filesは基本的に動いてくれてます。
nginxをreloadする度に彼はtryしてくれています!
try_filesが動かない場合はどこかでそのlocationが優先順位によって捕まっている事が多いです。

リダイレクトフラグ

リダイレクトフラグの指定によって、
ステータスコードを変えることができます。
指定できるのは下記のものとなっています。

▼LAST
rewriteディレクティブの処理を停止し、書き換えられたURIに対して最初から locationを再検索します。 また、別のlocationにあるrewriteディレクティブは実行されます。

▼BREAK
rewriteディレクティブの処理を停止し、locationの検索を停止します。 また、別のlocationにあるrewriteディレクティブは実行されません。

▼PERMANENT
恒久的なリダイレクトをするときに使用します。 HTTPのステータスコードは「301」を返します。

▼REDIRECT
一時的なリダイレクトをするときに使用します。 HTTPのステータスコードは「302」を返します。

こんな感じになります。↓

location /sample {
  rewrite ^ /hogehoge/sample/ permanent;
}

https://sample.jp/sample にcurlコマンドでアクセスしてみましょう。

$ curl https://sample.jp/sample -sIL

HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Thu, 19 Apr 2018 12:00:00 GMT
Content-Type: text/html
Content-Length: 
Location: https://sample.jp/hogehoge/sample/
Vary: User-Agent
Cache-Control: max-age=600, public

HTTP/2 200
server: nginx
date: Thu, 19 Apr 2018 12:00:00 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
vary: User-Agent
cache-control: max-age=600, public
alt-svc: clear

301で返ってきてリダイレクトしました。

(例:httpをhttpsにリダイレクトする設定

server {
  listen 80;
  server_name www.example.com;

  if ($http_x_forwarded_proto != https) {
    rewrite ^(.*)$ https://www.example.com$1 permanent;
  }
}

クライアントとロードバランサーの間で使用されたプロトコルを判別する
「X-Forwarded-Proto」

このリクエストヘッダーを使用することで
リダイレクトループを発生させずにhttpをhttpsにリダイレクトさせることができます。

組み合わせて使う

以上のことが主なエラー解決の鍵であることが多いです。
これらを組み合わせて、エラー箇所の対処をしたりします。

(例: https://sample.jp/samplehttps://samle.jp/sample/tsunoda/ にリダイレクト、かつ
内部で/hogehoge/sample/を読み込みたい。

location = /sample {
  rewrite ^ /sample/tsunoda/ permanent;
}
location = /sample/tsunoda/ {
  try_files $uri $uri/ /hogehoge/sample/$args;
}

こんな感じっしょ!
あれ、/hogehoge/sample/にURL変わっちゃうし、cssとかjsとか効いてないな…
書き直す↓

locaiton ~ /sample {
  rewrite ^/sample(.*)$ /hogehoge/sample$1 break;
}  
location = /sample {
  rewrite ^ /sample/tsunoda/ permanent;
}
location = /sampe/tsunoda/ {
  try_files $uri $uri/ /hogehoge/sample/$args;
}

え、今度はphpがダウンロードされる…(;´Д`)
書き直す↓

locaiton ~ /sample {
  rewrite ^/sample(.*)$ /hogehoge/sample$1 break;
}
location = /sample {
  rewrite ^ /sample/tsunoda/ permanent;
}
location = /sampe/tsunoda/ {
  try_files $uri $uri/ /hogehoge/sample/$args;
}
location = /hogehoge/sample/ {
  include /path/to/nginx/fastcgi-php.txt
}

これでようやく表示できるようになりました。
location = は完全一致です。
/sample(.*)$が来たら、/hogehoge/sample/のphp、cssなどを見る。
/sampleが来たら/sample/tsunoda/へリダイレクト
/sample/tsunoda/で内部リダイレクト
/hogehoge/sample/のphpにfastcgiの設定をincludeしてあげて表示する。
こんな感じで組み合わせると表示できます。

まとめ

locationの優先順位によってどこかで引っ張られていたり、
fastcgiの設定が通ってなかったり、
どうにもtry_filesが効かないなと思ったら他の箇所のlocationに引っかかってたり。

急にできて、え!何で!?…あーそういう事か!
なんて事もあったり。

・location
・fastcgi phpの読み込み
・try_files
・リダイレクトフラグ
ポイントを頭に入れておくとアプローチしやすいかなと思います。

rewriteは黒魔術とも言われているみたいですね!
かわいい女の子の手の平で転がされていると思えば、良いかw

参考URL nginxでリダイレクト