Windows Server 2012ではIIS 8。これがWindows Server 2012 R2ではIIS 8.5になります。
なにが変わるかって? Static Content (静的コンテンツ) のキャッシュコントロールヘッダーがなんか変わるんですね-しょぼん。
今回はぐぐっても錯綜した情報が垣間見える、ASP.NET MVCにおけるIISのStatic Contentに関するキャッシュコントロールヘッダーの変更についてです。
何気にIISでほげもげするのってあんまりまとまってないんですね。特にARR絡むと。
- Cache Control Header の確認
- IIS における Cache Control Header
- Custom Headers を設定する
- 拡張子に応じた Cacheの調整
- パスに応じたCache の調整
- ARRがあるときの web.config に注意
- まとめ
Cache Control Header の確認
Cache Control Headerを確認するなら、ChromeやIEの開発者ツールを使うと楽でしょう。たぶんきっと。
F12 > Networkタブ > 適当なコンテンツ > Headers > Response Header
こんな感じで応答ヘッダーが見えます。

Cache Control Header について
参考になるサイトです
ここを参考に、キャッシュさせたいもの、させたくないものにどんなCache Control Headerを設定すればいいのか頭を整理するといいでしょう。
IIS における Cache Control Header
web.config で設定しよう
IISのCache Control Headerですが、通常はIIS全体(ApplicationHost.config) ではなく、サイト単位(web.config)で行うでしょう。
web.configでやらない理由がないですね。
IIS 8.5 におけるデフォルトの Cache Control Header
まずCache Control Headerをそもそも付けるかどうかですが、これはSystem.Webディレクティブ配下のhttpRuntime要素であるsendCacheControlHeaderにbooleanで指定します。
デフォルトはtrueで、そのままだとCache Control Headerにprivateが送信されます。つまりCacheされるかはブラウザの挙動に依存します。1
https://msdn.microsoft.com/ja-jp/library/e1f13641%28v=vs.85%29.aspx
これはreference Sourceに記述の通りですね。

今回の場合は、Cache Control Headerは送信されないといけないのでそのままでいいでしょう。Privateの削除など制御は別途行います。
なので、逆に以下のようにsystem.webディレクティブにsendCacheControlHeader="false"がかかれていたらダメですね。
<system.web>
<httpRuntime targetFramework="4.5" sendCacheControlHeader="false" />
</system.web>
Cloud FrontでのCacheなどOriginのヘッダを尊重するObject Caching (CDN) の場合、むしろfalseである方が望ましかったりするのですが今回はスルーで。
Custom Headers を設定する
View単位の設定
Cache Control Headerをカスタムしたい。そんな時に、動的コンテンツ(ASP.NETはページをコードで制御する) なら各View側のコードで指定すればできます
全View統一の設定
全Viewで統一の動きならweb.configです。
そこで使うのが、system.webServerディレクティブ、 httpProtocolにあるcustomHeadersです。
https://msdn.microsoft.com/en-us/library/ms690556.aspx
https://www.iis.net/configreference/system.webserver/httpprotocol/customheaders
ここでは、任意のヘッダーを操作可能です。
例えば、X-Custom-Name: MyCustomValueをレスポンスヘッダーに追加するならこうです。
<configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="X-Custom-Name" value="MyCustomValue" /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
customHeadersは、add, remove, clearの各attributeを持っているので、これを使って自由に操作できますす。
たとえば、Cacheさせたくないなら以下のようにno-cache, no-store, must-revalidateを足せるでしょう。
Pragmaをなくすなどいろんな説があるのですが.... いったん付けておきます。
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
<add name="Pragma" value="no-cache" />
<add name="Expires" value="-1" />
</customHeaders>
</httpProtocol>
逆にCacheを常に有効にするなら、Cache-Controlに、Publicを付ければいいですね。Expiresなどで期限制御でしょう。
ただし、ここでやってしまうと全StaticContentに影響します。拡張子やパスごとに調整したくなったら更に調整が必要でしょう。
拡張子に応じた Cacheの調整
Custom HeadersでCache制御を書いたら、IIS referenceのcachingで調整ができます。逆にいうとCustom Headersで調整せずにやっても意味を成しません。
https://www.iis.net/configreference/system.webserver/caching
.aspの拡張子を対象にキャッシュ調整とかですね。
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".asp" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" />
</profiles>
</caching>
</system.webServer>
</configuration>
同様にKernelCacheが云々もできたりなんとか。
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="true" maxCacheSize="1000" maxResponseSize="512000"/>
</system.webServer>
</configuration>
まぁでも、ひと昔ならともかく拡張子制御は微妙ですね…。
パスに応じたCache の調整
拡張子がによるキャッシュ制御が微妙とみなす理由の1つが、現在はパスで静的コンテンツを区切るパターンが多いからです。実際minifyしてると拡張子がないファイルで展開することもありますしね。
そこで、全体ではCache Control を無効に *1しておいて、特定のパスだけ Cache を効かせる*2。といった、全体と一部パスで挙動を変化させる時に使えるのがlocation path=hogemogeです。
パスごとにsystem.webServerの調整ができたりするので、これを使えばパスごとのCache Control Headerを書き替えなおすこともできます。
例えば、全体ではCache Controlをno-store, no-cacheにしていても、 /static配下のコンテンツはキャッシュしたいなら、全体に対するsystem.webServerディレクティブの下に
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
<add name="Pragma" value="no-cache" />
<add name="Expires" value="-1" />
</customHeaders>
</httpProtocol>
</system.webServer>
pathを指定した記述をweb.configで追加すればいけます。/static配下ならこうです。
<location path="static">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="03:00:00" />
</staticContent>
<httpProtocol>
<customHeaders>
<remove name="Cache-Control" />
<remove name="Pragma" />
<remove name="Expires" />
<add name="Cache-Control" value="public" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
これでIIS/ASP.NETを使っている時のCache Control Headerは制御できますね。
ARRがあるときの web.config に注意
ARR - Application Request Routingを使うと、IIS自身でA WebSiteからB WebSiteにリダイレクト(URL書き換え / プロキシ)を組んだりが簡単にできるようになります。
https://www.iis.net/downloads/microsoft/application-request-routing
ARRを使うことで、ASP.NETの本体の前にいくつかのプロキシサイトを構成しておいて、「特定の状態の場合はアプリケーションまでルーティングさせない」などの構成が可能になります。結構神で素晴らしいわけです。
ProxySite - Application
ただし、ARRでルーティングするために作ったサイトはASP.NETなど本来やりたいWebサイトとは独立したWebサイトですよね。そのため、ASP.NET本体などの前にProxy用のWebサイトがある時、ASP.NETの応答が外側のプロキシサイトのweb.configで上書かれるんですねー。はい注意。
なので、ASP.NET側でweb.configを使って、あるいはコードでキャッシュ制御を書いても、プロキシサイトのweb.configを調整しておかないといけません。
知らなくてはまりましたのです。
まとめ
web.configかわいいよ。web.config *3
- Cache-Control: privateのみが指定されている場合、何らかのキャッシュへの記録されるおそれがある。実際Chromeではキャッシュされないのに他ではキャッシュされる↩