読者です 読者をやめる 読者になる 読者になる

動的なErrorDocumentに変数を渡してユーザごとにエラーを出し分ける方法

社内ブログに書いた内容をまるっと転載。


mod_proxyを使ってApacheを2段階にし、静的なコンテツはfrontend、動的なコンテンツはbackendというやり方をした場合、
backendのプロセスが落ちた際にfrontendで503エラーのページを返すことができます。


で、そのページをメンテナンスページなんかにしておいて、
「メンテナンス時にbackendのプロセスを落とすだけでメンテナンス状態にする」
っていう手抜きができたりするのですけど、今回これをユーザごとに出し分けたいなと思ったのです。


で、ApacheのErrorDocumentにパラメータを渡す方法をググったのですが、
探し方が悪いのか、全然見つからず困ってました。
「ErrorDocument パラメータ」「ErrorDocument querystring」「ErrorDocument グループ化」「ErroroDocument マッチした値」
とかとか。


まず元のErrorDocumentがどうなっているかと言うと、

<Location "/users/">
  ErrorDocument 503 /etc/maintenance.cgi
</Location>

こんな感じだと思ってください。


backendサーバが落ちている状態で/users/にリクエストが来ると503エラーになるため、
Locationで指定された上のErrorDocumentが呼び出されます。
で、/etc/maintenance.cgiの内容が返ると。


このとき、例えば/users/exampleというリクエストなら、この「example」をmaintenannce.cgiに渡したかったのです。
で、はじめに思いついたのがLocationMatchに変更して()でグルーピングして、その内容を使う方法。
でもこれはそもそもグルーピングした値を取り出せなそうなのでダメ。


次にSetEnnvIfで環境変数に代入してをそれを使う方法。

SetEnvIf Request_URI "^/users/(.*)$" user=$1

こんな感じでuserに値を入れたのですが、ErroDocumentの行で

  ErrorDocument 503 /etc/maintenance.cgi?%{ENV:user}

とかやっても%{ENV:user}という文字列がそのまま渡ってしまいダメでした。
この辺、mod_rewriteとのログ見ながら色々試行錯誤したのですけどどれも上手く行かず。
(/etc/maintenance.cgiをRewriteRuleに食わせるとかもやってみたんですけど、そのRuleが適用されるタイミングではパラメータが消えてました)


ただ、rewrite試すまで頭から抜けてたことなんですが、「ローカルなpathだけどcgiは実行されてる」ってことを思い出し、
原点に戻ってCGIの中からApache環境変数を見れば良いのかってことで覗き見。


そしたら「REDIRECT_user」という環境変数で入ってました。
といことで、最終的に以下のように書いて終了。

<Location "/users/">
  SetEnvIf Request_URI "^/users/(.*)$" user=$1
  ErrorDocument 503 /etc/maintenance.cgi
</Location>

追記追記ー。

上の例は実際はJavaScript用の出力でして、
<script>が200ステータスコードしか処理できないのを忘れてました。

といことで、CGIの中でステータスコード200を返すように追加しました。
CGIの中でステータスコードを吐くとそっちが優先されるようで。