mod_rewrite
Apache

mod_rewriteリダイレクトループの発生条件と対策

ここ最近似た話を聞いたので7年前の思い出を以下に備忘録。
(この時トラウマレベルのLoadAvarageに遭遇し順繰りLBから外れるWEBサーバの定期再起動の不毛な話が出たりしました)
リダイレクトで同じサーバ見さすと用心しないと簡単にループするらしいので以下のケースに限った話ではなさそう。ログ出して確認するのがよいことでしょう。

ループじゃなかった場合の切り分ける指標に↓以下を確認するとよさそうです。
システムの負荷の原因を切り分ける方法 - Qiita

preforkなら搭載メモリよりプロセスごとに使うメモリ×接続上限等が大きいとOOM_killerはふつうに出るだろうな等。
https://selfkleptomaniac.org/archives/2418

mod_rewriteループ発生の条件と対策

ちょっと苦しんだ問題を忘れないように今後の参考に書いておきます。
Apacheはコンパイルしていれた2.2系でした。
以下開発の人とログレベル調整したりしてつきとめて頂いたもの

===
rewriteの処理で次の条件のときrewrite処理で無限ループとなり高負荷を引き起こしていることがわかりました。

■条件
  • クローラ用置換文字列(A-DE-QS-SD-TQ-N)が含まれていて
  • rewriteにより元のURIに戻したときのURIのCGIが存在しない
■暫定対処

ほかに対応策が思いつかなかったのですが、現状のrewriteルールで再帰的[N]にrewriteす
るようになっていてこれが無限ループを引き起こすと思われますので、
rewriteルールをベタ書きに変更し暫定対処としたほうがよいかもしれません。

●現状のrewriteルール
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2 [N]
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2 [N]
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2 [N]
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2 [N]
 RewriteRule ^(.*)_Q-N_(.*)$ $1?$2 [L]

  ↓

●暫定対処のrewriteルール
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2
 RewriteRule ^(.*)_A-D_(.*)$ $1&$2
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2
 RewriteRule ^(.*)_E-Q_(.*)$ $1=$2
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2
 RewriteRule ^(.*)_S-S_(.*)$ $1/$2
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2
 RewriteRule ^(.*)_D-T_(.*)$ $1.$2
 RewriteRule ^(.*)_Q-N_(.*)$ $1?$2 [L]

===
その後、ロードバランサ(アプライアンス)でユーザエージェント見てクローラーだったら特定のWEBサーバに飛ばすように対策しました。

他にmod_rewriteでは、CMSのモジュール同士の相性の問題というか、
各モジュールがリダイレクトしあってループしたりなんてのも遭遇したことがあります。
(その時の対策は、片方のモジュールの利用をあきらめる、というものでした。)

まずはRewriteのログレベルを調整して確認してみるのが対策の初手と思われます。

httpd.conf
RewriteLog "/var/log/httpd/rewrite.log"
RewriteLogLevel 9

出力されたログを見てみる。9だと結構量がでるので調査後にはoffにするのが必須です。
http://www.lesstep.jp/step_on_board/apache/402/

ループでなくクローラー対策としてSEO的に問題なければ国外(素行の悪質なクローラーがくる国)からのIPをFW的な何かでブロックするとよさそうです。

参考:

http://www.nurs.or.jp/~sug/homep/rewrite/rewrite1.htm
http://tm.root-n.com/server:apache:module:rewrite
http://mikeneko.creator.club.ne.jp/~lab/web/htaccess/redirect.html

ロードバランサ使うケースのリダイレクトループについてここらへんに。
https://qa.atmarkit.co.jp/q/3221

mod_rewrite自体の解説
http://blog.dawgsdk.org/weblog/archives/411011
http://koseki.hatenablog.com/entry/20090611/ModRewrite

システムの負荷の原因を切り分ける方法 - Qiita

そのた

nginxだと「X-Forwarded-Proto」リクエストヘッダーでif分岐で制御できたりする模様。
https://www.skyarch.net/blog/?p=7088

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

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

nginxの場合のデバッグ方法。
https://server-setting.info/centos/nginx-rewrite-debug.html

server {
    server_name www.example.com;
    ...
    error_log   /var/log/nginx/example-error.log notice;
    rewrite_log on;
    ...
}

ログレベルの段階を制御するのは上の例だとnoticeで、error_logディレクティブのデフォルトはerrorらしい。
severityの意味合いと重大度はsyslogと一緒の模様。rewriteの動確するにはnotice以上じゃないと出ないそうです。