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の設定を一度見直してみるのがいいかもしれません。

このブログ記事について

このページは、Masahiro Naganoが2009年7月16日 17:29に書いたブログ記事です。

ひとつ前のブログ記事は「MovableType 4.2でのhatena starの付け方」です。

次のブログ記事は「VMware ESXiで電力管理を有効にする」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja