mod_rewriteで503
ちょっと気になったので。。。
mod_rewriteのRフラグではステータスコードを一緒に指定できます。mod_rewrite - Apache HTTP Server Version 2.2にも
最初、ここら辺のすべての処理をmod_rewriteだけで実現できるかなと思ったのですが、残念ながら、mod_rewriteでは300番系のエラーを出すことができますが、503のエラーは出せないようなのでさくっと簡単なものを作るの「には」便利とかいわれてるPHPで、さくっと、503を出すことにしました。
と「300〜400の範囲で任意のステータスコードを指定できる」とあります。
'redirect|R [=code]' (force redirect)
Prefix Substitution with http://thishost[:thisport]/ (which makes the new URL a URI) to force a external redirection. If no code is given, a HTTP response of 302 (MOVED TEMPORARILY) will be returned. If you want to use other response codes in the range 300-400, simply specify the appropriate number or use one of the following symbolic names: temp (default), permanent, seeother. Use this for rules to canonicalize the URL and return it to the client - to translate ``/~'' into ``/u/'', or to always append a slash to /u/user, etc.
が、「300〜400の範囲だけ」とは書いてないし、「ソースはどうなってんの?」ということで、ちょっと追ってみました。
これ以降、Apache2.2.4での話です。Apache1.3系・2.0系では確認していません。
以下はApache2.2.4のmodules/mappers/mod_rewrite.cの抜粋です。最初のcaseは3297行目で、設定内容を解析しているコードの一部分です。
static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg, char *key, char *val) { : case 'r': case 'R': if (!*key || !strcasecmp(key, "edirect")) { /* redirect */ int status = 0; cfg->flags |= RULEFLAG_FORCEREDIRECT; if (strlen(val) > 0) { if (strcasecmp(val, "permanent") == 0) { status = HTTP_MOVED_PERMANENTLY; } else if (strcasecmp(val, "temp") == 0) { status = HTTP_MOVED_TEMPORARILY; } else if (strcasecmp(val, "seeother") == 0) { status = HTTP_SEE_OTHER; } else if (apr_isdigit(*val)) { status = atoi(val); if (status != HTTP_INTERNAL_SERVER_ERROR) { int idx = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR); if (ap_index_of_response(status) == idx) { return apr_psprintf(p, "RewriteRule: invalid HTTP " "response code '%s' for " "flag 'R'", val); } } if (!ap_is_HTTP_REDIRECT(status)) { cfg->flags |= (RULEFLAG_STATUS | RULEFLAG_NOSUB); } } cfg->forced_responsecode = status; } } else { ++error; } break; : }
apr_isdigit以下が
[R=503]
のようにステータスコードを数字で指定したときの処理になりますが、基本的に指定されたステータスコードの有効性だけチェックして、HTTP_INTERNAL_SERVER_ERRORの場合だけ特殊扱いしてる感じです(間違いがあれば指摘して下さい :-))。
ということで、動作確認してみました。設定は適当w
RewriteEngine On RewriteLog logs/rewrite_log RewriteCond %{QUERY_STRING} rewrite RewriteRule '' '' [R=503,L]
apache graceful後、ブラウザから確認。
Service Temporarily Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
おおーっ!動くんじゃん。HTTPレスポンスヘッダは以下のような感じ。
HTTP/1.x 503 Service Temporarily Unavailable Date: Mon, 09 Jul 2007 06:27:47 GMT Server: Apache/2.2.4 (Unix) mod_ssl/2.2.4 OpenSSL/0.9.7a PHP/5.2.3 Content-Length: 433 Connection: close Content-Type: text/html; charset=iso-8859-1
ついでに、
RewriteRule '' '' [R=501,L]
で確認。
Method Not Implemented
GET to / not supported.
おおーっ!初めて見たよ、このメッセージ!w
もう一つついでに、
RewriteRule '' '' [R=500,L]
で確認。
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator, xxxx@xxxxxx.xxx and inform them of the time the error occurred, and anything you might have done that may have caused the error.
More information about this error may be available in the server error log.
なんだ、いけんじゃん。。。
あとは、ErrorDocumentディレクティブを
ErrorDocument 503 /path/to/sorry.html
のような感じで設定してやればOKです。
追記(2007/07/09 16:20)
Apache2.0.59のmod_rewrite.cのソース(836行目)を見てみました。こちらは「リダイレクト系のステータスコードのみ」に限定してますね。
static const char *cmd_rewriterule_setflag(apr_pool_t *p, rewriterule_entry *cfg, char *key, char *val) { int status = 0; int i; if ( strcasecmp(key, "redirect") == 0 || strcasecmp(key, "R") == 0 ) { cfg->flags |= RULEFLAG_FORCEREDIRECT; if (strlen(val) > 0) { if (strcasecmp(val, "permanent") == 0) { status = HTTP_MOVED_PERMANENTLY; } else if (strcasecmp(val, "temp") == 0) { status = HTTP_MOVED_TEMPORARILY; } else if (strcasecmp(val, "seeother") == 0) { status = HTTP_SEE_OTHER; } else if (apr_isdigit(*val)) { status = atoi(val); } if (!ap_is_HTTP_REDIRECT(status)) { return "RewriteRule: invalid HTTP response code " "for flag 'R'"; } cfg->forced_responsecode = status; } }