Apacheの REQUEST_URI で勘違いしていた話
2020-12-24 - 相本 智仁
Apacheの REQUEST_URI で勘違いしていた話
Apache設定時等に出てくる REQUEST_URI
の値は QueryString が「含まれるケース」と「含まれないケース」があります。
例えば次のようにアクセスしたときに REQUEST_URI
の値は「/cgi-bin/env.cgi?test=1」「/cgi-bin/env.cgi」どちらなのかというお話です。
- http://www.example.co.jp/cgi-bin/env.cgi?test=1
具体的には次のとおりです。
QueryString が「含まれるケース」
- php や CGI で使用可能な Apache がセットした
環境変数REQUEST_URI
- 特に設定なし(デフォルト)の場合であり、Apache の設定により QueryString を含めなくすることも出来ます。
QueryString が「含まれないケース」
- mod_rewrite で使用する
%{REQUEST_URI}
- mod_setenvif で使用する
%{REQUEST_URI}
- Ifディレクティブ などの expr で使用する
%{REQUEST_URI}
- mod_ssl の環境変数(例: mod_headers では
%{REQUEST_URI}s
と指定して使用)
調べた経緯
ある時「アクセスURL の拡張子が mp4 以外のときだけに特定の処理を行いたい」という要件がありました。 Ifディレクティブ を使って以下のように出来そうです。
<If "%{REQUEST_URI} !~ m!\.mp4$!i">
[やりたい処理]
</IF>
私はこの設定の場合 QueryString が含まれたアクセスでは「Ifが真にならない」と思っていました。 しかし、実際に動作確認をしてみると QueryString が含まれたアクセスをしても If が真になり 要件を満たす動作になります。
php で 環境変数REQUEST_URI を表示する
一方で CGI や php で Apache がセットする 環境変数REQUEST_URI
を表示すると QueryString が含まれます。
- 例えば次の index.php を作成します。
<?php
print $_SERVER['REQUEST_URI'];
?>
このファイルに対して「http://localhost/index.php?a=b」とアクセスすれば「/index.php?a=b」と表示されます。
環境変数REQUEST_URI
はアクセス URL の QueryString が含まれた値であることが分かります。
私はもともとこの「 環境変数REQUEST_URI
にセットされる値は QueryString が含まれる」と頭にあり、
上記の Ifディレクティブ では「真にならない」と思っていました。
そして IFディレクティブ の動作から REQUEST_URI
は使うケースによって値が違うことに気が付きました。
どのような仕様なのか Apache
のソースコードで確認しました。1
php や CGI で使うREQUEST_URI
php や CGI に渡す 環境変数REQUEST_URI
は Apache のどんな値が渡るのかを見てみます。
- server/util_script.c
381 AP_DECLARE(void) ap_add_cgi_vars(request_rec *r)
382 {
〜
386 int request_uri_from_original = 1;
〜
394 if (conf->cgi_var_rules) {
395 request_uri_rule = apr_hash_get(conf->cgi_var_rules, "REQUEST_URI",
396 APR_HASH_KEY_STRING);
397 if (request_uri_rule && !strcmp(request_uri_rule, "current-uri")) {
398 request_uri_from_original = 0;
399 }
400 }
401 apr_table_setn(e, "REQUEST_URI",
402 request_uri_from_original ? original_uri(r) : r->uri);
401行目が環境変数REQUEST_URI
値をセットする処理です。
変数request_uri_from_original の値が真なら 関数original_uri(r) の結果が、偽なら r->uri
がセットされます。
関数original_uri はリクエストラインの URI に該当する部分を返す関数です。従ってこれは QueryString が含まれた値 です。
一方 r->uri
は、URI のパス部分のみの値です。QueryString
が含まれていない値です。2
変数request_uri_from_original の値は、394行目から400行目の処理により決まりますがこれは設定ディレクティブ CGIVar
の設定値3で決まります。
設定がない場合は 1 となりますので、環境変数REQUEST_URI
は QueryString が含まれた値がセットされることがわかります。
次のように設定すると環境変数REQUEST_URI
に r->uri
がセットされることになり、デフォルト動作から変わります。
CGIVar REQUEST_URI current-uri
expr で使う REQUEST_URI
Apache の設定の様々な箇所で使用可能な expr で REQUEST_URI
を指定した場合 Apache のどの値が渡るのかを見てみます。
- server/util_expr_eval.c
1315 static const char *request_var_names[] = {
1316 "REQUEST_METHOD", /* 0 */
1317 "REQUEST_SCHEME", /* 1 */
1318 "REQUEST_URI", /* 2 */
1319 "REQUEST_FILENAME", /* 3 */
1320 "REMOTE_HOST", /* 4 */
〜
1349 static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1350 {
1351 int index = ((const char **)data - request_var_names);
〜
1356 switch (index) {
1357 case 0:
1358 return r->method;
1359 case 1:
1360 return ap_http_scheme(r);
1361 case 2:
1362 return r->uri;
REQUEST_URI
は 2 で、r->uri
の値が使われています。
SetEnvif で使う REQUEST_URI
SetEnvifディレクティブ で REQUEST_URI
を指定した場合 Apache のどの値が渡るのかを見てみます。
- server/util_expr_eval.c
364 else if (!strcasecmp(fname, "request_uri")) {
365 new->special_type = SPECIAL_REQUEST_URI;
366 }
〜
536 case SPECIAL_REQUEST_URI:
537 val = r->uri;
538 break;
設定で request_uri
(大文字小文字区別なし)が指定されると、special_type
が SPECIAL_REQUEST_URI
となり
SPECIAL_REQUEST_URI
は、 r->uri
の値が使われています。
mod_rewrite で使う REQUEST_URI
mod_rewrite で REQUEST_URI
を指定した場合 Apache のどの値が渡るのかを見てみます。
- modules/mappers/mod_rewrite.c
2114 case 'U':
2115 if (!strcmp(var, "REQUEST_URI")) {
2116 result = r->uri;
2117 }
2118 break;
2119 }
r->uri
の値が使われます。
まとめ
ソースコードを見ると 環境変数REQUEST_URI
にセットする時だけ、違う値がセットされることがわかります。
そしてそれも設定により r->uri
にすることが出来ることもわかりました。
一つの設定箇所でこう動いたからといって、他の箇所でも同じように動くとは限らない、 思い込みに注意しなければと思った出来事でした。