Apacheのmod_headersは、リクエストやレスポンスのヘッダを操作できる便利モジュールなのです。 例えば、レスポンスにとあるヘッダを追加するのあれば、
Header set X-Powered-By OreOre
とHeader setを追加します。
逆に、削除するのであれば、
Header unset X-Powered-By
とします。簡単です。
しかし、上記のHeader set設定だけでは、HTTPのレスポンスコードが200 OKの場合にのみ、Headerが追加されて、500系、400系のエラー、またはRedirectなどの300系のレスポンスの場合には有効にはなりません。
200以外の場合でもHeaderの追加を行なう場合には alwaysオプションを追加します
Header always set X-Powered-By OreOre
これで、Redirectなどの場合でもHeaderが付いてブラウザへレスポンスすることができます。
ところが、unsetの場合は逆にalwaysを付けると、200
OKの場合にだけ有効になり、300、400、500系のHTTPステータスでは、Headerが削除されずにブラウザへ流れうことがあります。
どういうことでしょう。
これには、Apacheの持つheaders_outと、err_headers_outという2つの構造体が関わっています。レスポンスにHeaderを加える際は、この2つの構造体に追加することになります。err_headers_outは文字通り、エラー時のHeaderとして出力されます。エラーではない時にはheaders_outとerr_headers_outがマージされて出力されます。
少し難しいですが、ここで言うエラー時というのはApacheの内部の状態であり、レスポンスのステータスコードではないということです。
mod_perlのHandlerで試してみましょう
■SamleHandler.pm
package SampleHandler;
use Apache2::Const -compile => qw(OK HTTP_NOT_FOUND);
use Apache2::RequestRec ();
sub handler : method {
my ($class, $r) = @_;
$r->headers_out->add('X-Powered-Always','OreOre');
$r->headers_out->add('X-Powered-Onsuccess','OreOre');
$r->status(404);
$r->content_type('text/plain');
$r->print( "not found" );
return Apache2::Const::OK;
}
1;
■httpd.conf
Header always unset X-Powered-Always
Header unset X-Powered-Onsuccess
<Location /sample>
SetHandler perl-script
PerlResponseHandler SampleHandler
</Location>
SampleHandlerは404を返すだけのHandlerです。
しかし、Handlerの戻り値はOKであり、Apache内部的にはエラーと扱いません。この場合、どのようなレスポンスがブラウザに返るのでしょう。curlを使って確認します。
$ curl -v http://localhost:8080/sample
> GET sample HTTP/1.1
> User-Agent: curl/7.18.2 (x86_64-redhat-linux-gnu) libcurl/7.18.2 NSS/3.12.0.3 zlib/1.2.3 libidn/0.6.14 libssh2/0.18
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Thu, 16 Jul 2009 08:11:52 GMT
< Server: Apache
< X-Powered-Always: OreOre
< Transfer-Encoding: chunked
< Content-Type: text/plain
<
not found
alwaysオプションを付加してないX-Powered-Onsuccessは消えましたが、X-Powered-Alwaysは削除されませんでした。
これはなぜかというと、mod_headersの実装において、alwaysオプションを付けた時にはerr_headers_outの操作のみを行なうからです。
alwaysというと、エラーでもそうでなくても有効になりそうですが、このようにどの、headers_out構造体に対して効き目があるのか、考えてみる必要があります。Apacheのドキュメントにも書いてありました。
オプションの condition は onsuccess か always のどちらかを指定できます。これは内部ヘッダテーブルのどれを 操作するかを決定します。onsuccess は 2xx ステータスコードの、always は全てのステータスコード (2xx を含む) の意味になります。 あるモジュールでセットされるヘッダをアンセットしたい場合は特に、 どのテーブルが影響を受けるかを実際に試したほうがよいでしょう。
実際、これが問題となるのは、mod_perlのHandlerだけではありません。大規模なWebアプリケーションには必ず用いられるReverseProxyでも同じことが起きます。
このような環境で、Headers unsetを行なっている場合は、Apacheの設定を一度見直してみるのがいいかもしれません。