しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

IIS と Vary ヘッダと HTTP 圧縮モジュールのバグ

id:Waquesseix
この記事を読んで早速vary: user-agentを追加しようと思ったら、なんかバグでvaryが変えられない(圧縮をオフにしない限り)みたいでがっくりしました。。。

ASP.NET でカスタム HTTP ヘッダを追加する方法 - しばやん雑記

そういえば、ASP.NET も Vary ヘッダの値がアスタリスクになってしまう件とかあったなーと思って調べたら、コメントの通り IIS の圧縮モジュールのバグみたいですね。

IIS: gzip compression filter removes pre-existing vary: header | Microsoft Connect

Connect に上がっている報告を読むと、IIS の HTTP 圧縮を行うネイティブモジュールが Vary を追加する時に、既に追加済みの値を考慮せず上書きしてしまうことが原因のようです。IIS と ASP.NET ってこんな問題が割と多い気がしますね。*1

多くのレンタルサーバではリソースを考慮して、デフォルトでは HTTP 圧縮が有効になっていないと思いますが、Windows Azure の利用や開発サーバの IIS Express 化で HTTP 圧縮が有効になっていたりすることもあるかもしれません。

とりあえず今回は圧縮を無効にして Vary に User-Agent を指定する方法を紹介しておきます。まずは OutputCache の設定を行います。

[OutputCache(Duration = 10, VaryByHeader = "User-Agent")]
public ActionResult Index()
{
    return View();
}

User-Agent を基準にキャッシュの出し分けをしたいので VaryByHeader プロパティを使って、使用するヘッダ名を設定します。

この例は ASP.NET MVC の場合ですが、参考までに Web Forms の場合は以下のように書きます。

protected void Page_Load(object sender, EventArgs e)
{
    // Vary: User-Agent を出力する
    Response.Cache.VaryByHeaders.UserAgent = true;
}

どうでもいいですが、Response.Cache プロパティを使う方法は ASP.NET MVC でも有効です。

そして、このままだと HTTP 圧縮が有効になっている場合は Vary: Accept-Encoding と固定になってしまうので、圧縮を無効にするために Web.config に以下の要素を追加します。

<system.webServer>
  <urlCompression doStaticCompression="false" doDynamicCompression="false" />
</system.webServer>

これで圧縮が無効になるので、設定した通りの Vary ヘッダが出力されるようになります。

f:id:shiba-yan:20130520223602p:plain

ポーン(  Д )⌒Y⌒Y⌒Y⌒Y⌒Y⌒...。....。コロコロ

Vary の値がアスタリスクとなってしまう場合には、Web.config で出力しないように設定すれば OK です。

<system.web>
  <caching>
    <outputCache omitVaryStar="true" />
  </caching>
</system.web>

ちなみにコードからも設定できます。

// Vary: * を出力しない
Response.Cache.SetOmitVaryStar(true);

omitVaryStar が true の場合にはアスタリスクが出力されないので、今度こそ設定した通りに Vary ヘッダが出力されるようになりました。

f:id:shiba-yan:20130520223859p:plain

理想は IIS の圧縮モジュールのバグが修正されることなんですが、とりあえずの対応としては無効化する方法しかないようですね。

追記

すっかり追いかけることも忘れ、久し振りに Connect サイトを見ると上のバグは修正済みとなっていました。既に Hotfix がリリースされています。

Vary header is overwritten as "Accept-Encoding" after you enable dynamic content compression in Windows

恐らくは IIS 10.0 では直っていますが、Azure Web Apps の動的圧縮モジュールのバージョンを調べたところ、修正されたバージョンではなさそうでした。

*1:とりあえず Connect で投票と再現について回答しておきました