それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Apacheのmod_rewriteによるURL書換についてのまとめ(2)RewriteCond の変数と条件

mod_rewrite の書き方についてまとめ2

前回のおさらい

  • RewiteRule はリクエストURIに対して正規表現置換
  • RewiteRule は複数書くと、全部処理され最後にマッチしたものが採用
  • RewiteCond はRewriteRuleとAnd条件で追加される。

RewriteRule を1つ、RewriteCondを複数

1つのRewriteRuleに対して、RewriteCondを複数書くとどうなるのか。

RewriteCond %{REQUEST_FILENAME} \.jpg
RewriteCond %{REQUEST_FILENAME} hoge
RewriteRule  ^  ../get.php?from=ex1-rule1
hoge.gif は 書換処理されない。

hoge でマッチするが、jpg にマッチしないのでAND条件で、最終的にFalse

その結果、通常リクエストが行われ、hoge.gif が存在しないので404 file not found になる。

takuya@~/Sites/rewrites/ex1$ curl -I   http://localhost/~takuya/rewrites/ex1/hoge.gif
HTTP/1.1 404 Not Found # 書換処理されない。
hogehoge.jpg や hoge.jpg へのリクエスト

ファイルは存在しないが、リクエストURIにRewriteが行われるので、get.php に転送される。

takuya@~/Sites/rewrites/ex1$ curl    http://localhost/~takuya/rewrites/ex1/hoge.jpg
array(1) {
  ["from"]=>
  string(9) "ex1-rule1"
}
takuya@~/Sites/rewrites/ex1$ curl    http://localhost/~takuya/rewrites/ex1/hogehoge.jpg
array(1) {
  ["from"]=>
  string(9) "ex1-rule1"
}

RewriteCond は複数書くとAND条件

RewriteRule AND RewriteCond 

複数書けば

RewriteRule AND RewriteCond AND RewriteCond

さらに、Rewriteruleが複数なら

if ( RewriteRuleMatch AND RewriteCond  ) {   }
if ( RewriteRuleMatch AND RewriteCond AND RewriteCond ) {}

のようになっている。

後半で言及するけど、 [OR]を使えば

if ( RewriteRuleMatch AND (RewriteCond OR RewriteCond ) ) {}

のような処理も可能ですね。

RewriteRule のGET文字列の取扱い

RewriteRule の書換えでGETを指定した場合。

RewriteRule  ^  ../get.php?from=ex1

リクエストURLにつけたGET文字列は無視される

リクエスト結果

takuya@~/Sites/rewrites$ curl    http://localhost/~takuya/rewrites/ex1/hoge.jpg?test=1 # GET文字列つけた
array(1) {
  ["from"]=>
  string(3) "ex1" # 書換の結果に出てこない。
}

RewriteRule の書換えにGET指定しない場合

何もか書かずにURLだけを指定した場合、GET文字列の書換は起こらない。

RewriteRule  ^  ../get.php

リクエスト結果

takuya@~/Sites/rewrites$ curl    http://localhost/~takuya/rewrites/ex1/hoge.jpg?test=1 # GET文字列つけた
array(1) {
  ["test"]=>
  string(1) "1"
}

これはQueryString環境変数が書換指定が無いため、環境変数の書換が行われないと理解しています。

RewriteRule の書換えにGET指定し、QSA(クエリ文字列追記)フラグを書いた場合

QSA : Query String Append フラグをつけた場合は、GET文字列の書換が行われる。

GET引数が追加される例
RewriteRule  ^  ../get.php?from=ex1 [QSA]

リクエスト結果

都合よく、QueryStringが追記が行われるので便利そう。

takuya@~/Sites/rewrites$ curl    http://localhost/~takuya/rewrites/ex1/hoge.jpg?test=1
array(2) {
  ["from"]=>
  string(3) "ex1"
  ["test"]=>
  string(1) "1" # 書換先でGET文字列は追加された。
}
同名で上書きの例

RewriteRuleと同じGET引数を指定した場合も考えておく。

リクエストとRewriteRuleで同名の変数が指定されたら、同名の from=ex1 がリクエストで上書きされる。

takuya@~/Sites/rewrites$ curl    'http://localhost/~takuya/rewrites/ex1/hoge.jpg?test=1&from=curl'
array(2) {
  ["from"]=>
  string(4) "curl" # リクエストで上書き
  ["test"]=>
  string(1) "1"
}

リクエストの引数が強くて、RewriteRuleのGET引数は上書きされ消される。

このことから、GET文字列の書換は環境変数QueryStringに依存していると理解している。

リクエストのURLは、QueryStringとPathINFO でそれぞれ別に処理されているようだ。

RewriteRuleで使えるオプション

[QSA] の他にも、RewriteRuleで使えるオプションはたくさんある。ありすぎて、組合せが用意に想像つかないくらい。たくさん。

ApacheのDocumentationによると次表の通り。

Flag and syntax Function
B Escape non-alphanumeric characters before applying the transformation.
chain|C Rule is chained to the following rule. If the rule fails, the rule(s) chained to it will be skipped.
cookie|CO=NAME:VAL Sets a cookie in the client browser. Full syntax is: CO=NAME:VAL:domain[:lifetime[:path[:secure[:httponly]]]]
discardpath|DPI Causes the PATH_INFO portion of the rewritten URI to be discarded.
env|E=[!]VAR[:VAL] Causes an environment variable VAR to be set (to the value VAL if provided). The form !VAR causes the environment variable VAR to be unset.
forbidden|F Returns a 403 FORBIDDEN response to the client browser.
gone|G Returns a 410 GONE response to the client browser.
Handler|H=Content-handler Causes the resulting URI to be sent to the specified Content-handler for processing.
last|L Stop the rewriting process immediately and don't apply any more rules. Especially note caveats for per-directory and .htaccess context (see also the END flag).
next|N Re-run the rewriting process, starting again with the first rule, using the result of the ruleset so far as a starting point.
nocase|NC Makes the pattern comparison case-insensitive.
noescape|NE Prevent mod_rewrite from applying hexcode escaping of special characters in the result of the rewrite.
nosubreq|NS Causes a rule to be skipped if the current request is an internal sub-request.
proxy|P Force the substitution URL to be internally sent as a proxy request.
passthrough|PT Forces the resulting URI to be passed back to the URL mapping engine for processing of other URI-to-filename translators, such as Alias or Redirect.
qsappend|QSA Appends any query string from the original request URL to any query string created in the rewrite target.
redirect|R[=code] Forces an external redirect, optionally with the specified HTTP status code.
skip|S=num Tells the rewriting engine to skip the next num rules if the current rule matches.
type|T=MIME-type Force the MIME-type of the target file to be the specified type.

使い方例は、別章にまとめて書きます。

RewriteCond を複数ORで組み合わせる

RewriteRuleはほどほどにして、RewriteCondに戻ります。RewriteCondを複数書いた時にORで結合する話です。

例:UserAgentがcurlまたはtakuyaの時だけ処理をする

RewriteCond %{HTTP_USER_AGENT} curl [NC,OR]
RewriteCond %{HTTP_USER_AGENT} takuya [NC]
RewriteRule  ^  ../get.php?from=ex1

リクエストすると、書換が動作する。

takuya@~/Sites/rewrites$ curl    http://localhost/~takuya/rewrites/ex1/index.php
array(1) {
  ["from"]=>
  string(3) "ex1"
}
takuya@~/Sites/rewrites$ curl \
 -H "User-Agent: takuya" \
  http://localhost/~takuya/rewrites/ex1/index.php
array(1) {
  ["from"]=>
  string(3) "ex1"
}

UserAgent を指定してリクエストを送信してみる。

マッチしないUserAgentだと書き換えられず、本来の結果が表示される。

takuya@~/Sites/rewrites$ curl \
-H "User-Agent: webkit" \
  http://localhost/~takuya/rewrites/ex1/index.php

Hello from 'index'

もっとも、正規表現のマッチでORを使う理由はあまり無い。(正規表現複数条件をかけるため)

RewriteCond で 複数条件

ORを使って柔軟な条件を考えてみようと思います。

Cookie に login=true が含まれる OR UserAgentがcurl の時に書換

RewriteCond %{HTTP_USER_AGENT} curl  [NC,OR]
RewriteCond %{HTTP_COOKIE}  login=true
RewriteRule  ^  ../get.php?from=ex1
login=true を満たす

条件のCookie 側にマッチした場合。書換が起こる。

takuya@~/Sites/rewrites$ curl \
--cookie "login=true" \
-H "User-Agent: takuya" \
  http://localhost/~takuya/rewrites/ex1/index.php
array(1) {
  ["from"]=>
  string(3) "ex1"
}
UserAgent = curl を満たす。

条件のUserAgent = /curl/ 側を満たす場合。書換が起こる

takuya@~/Sites/rewrites$ curl \
--cookie "login=false" \
-H "User-Agent: curl " \
  http://localhost/~takuya/rewrites/ex1/index.php
array(1) {
  ["from"]=>
  string(3) "ex1"
}
どちらにもマッチしない場合。

どちらにもマッチしない場合、書換は処理されない。

takuya@~/Sites/rewrites$ curl \
--cookie "login=false" \
-H "User-Agent: takuya" \
  http://localhost/~takuya/rewrites/ex1/index.php
Hello from 'index'

ただし、この場合でもRewriteRuleが動作している。RewriteRuleにマッチするURLなので、RewriteCondルール処理判定されている。

RewriteCond で使えるオプション

オプション 意味
OR 直後に続くCondとOR
NC 大文字小文字を否区別

NC と OR を同時に指定するときは、[ NC, OR ] とカンマ区切りで指定する。

# 正しい例
RewriteCond %{HTTP_USER_AGENT} curl [NC,OR]
# 間違い ー [ OR ] は処理されない
RewriteCond %{HTTP_USER_AGENT} curl [NC]  [OR]

RewriteCond で使える環境変数

ApacheのDocumentation を閲覧すると、利用できる環境変数は次のものが挙げられている。 またRewriteCondで環境変数の書換や作成が可能なので、柔軟に対応ができる。

HTTP headers: connection & request:
HTTP_USER_AGENT
HTTP_REFERER
HTTP_COOKIE
HTTP_FORWARDED
HTTP_HOST
HTTP_PROXY_CONNECTION
HTTP_ACCEPT
REMOTE_ADDR
REMOTE_HOST
REMOTE_PORT
REMOTE_USER
REMOTE_IDENT
REQUEST_METHOD
SCRIPT_FILENAME
PATH_INFO
QUERY_STRING
AUTH_TYPE
server internals: date and time: specials:
DOCUMENT_ROOT
SERVER_ADMIN
SERVER_NAME
SERVER_ADDR
SERVER_PORT
SERVER_PROTOCOL
SERVER_SOFTWARE
TIME_YEAR
TIME_MON
TIME_DAY
TIME_HOUR
TIME_MIN
TIME_SEC
TIME_WDAY
TIME
API_VERSION
THE_REQUEST
REQUEST_URI
REQUEST_FILENAME
IS_SUBREQ
HTTPS

上記に記載されてないヘッダへのマッチ。

RewriteCond %{HTTP:X-MY-HEADER} curl  [NC]
RewriteRule  ^  ../get.php?from=ex1

HTTPのカスタムヘッダで、独自定義のヘッダにマッチさせるには %{HTTP:X-SAMPLE}のようにする。

$HTTP 変数がハッシュになっていて キーで取得しているイメージ

%{HTTP:X-SAMPLE} は $HTTP["X-SAMPLE"]   に相当

これを実際に記述して実行してみた結果、ヘッダ追加したばあいに書換が実行される。

takuya@~/Sites/rewrites$ curl -H "X-MY-HEADER: curl "   http://localhost/~takuya/rewrites/ex1/index.php
array(1) {
  ["from"]=>
  string(3) "ex1"
}

独自ヘッダでもRewriteCond+RewriteRuleの書換ルールセットは処理される。

RewriteCond で使える、マッチ演算

マッチ演算にはいくつかのパターンがあり、ApacheのDocumentから拾い出すと次表の通りでした。

比較演算子  内容
'<CondPattern' (lexicographically precedes)
'>CondPattern' (lexicographically follows)
'=CondPattern' (lexicographically equal)
'-d' (is directory)
'-f' (is regular file)
'-s' (is regular file, with size)
'-l' (is symbolic link)
'-x' (has executable permissions)
'-F' (is existing file, via subrequest)
'-U' (is existing URL, via subrequest)

よく使いそうなものは、! 否定-f ファイルが存在スル だと思います。

RewriteRuleと RewriteCond で紹介しなかったもの

書き換え後のURLに、さらにマッチさせる、サブリクエストにマッチ

書き換え後のURLを、さらに1からマッチをやり直すリクエストなど

サブリクエスト関連については、省略した。

また、プロキシリクエストに関しても省略した。

理由は、RewriteRuleの基本の流れと実験方法がわかれば、理解が容易になるため。

次章

次は、RewriteCondとRewriteMapで独自のプログラムを使う話を書こうと思います。