I recently encountered a puzzling scenario where IIS 7 was returning 304 Not Modified responses even when:
- No conditional headers (If-Modified-Since/If-None-Match) were present in the request
- The client explicitly sent Cache-Control: no-cache
- The resource wasn't cached on the client side
The issue stems from IIS 7's kernel cache (http.sys) behavior which can independently serve 304 responses without checking upstream caches. Key factors:
- Kernel caching is enabled by default for static content
- ETag validation occurs at kernel level
- The cache honors server-side expiration settings over client directives
After extensive testing, here's the configuration that resolves this:
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
</profiles>
</caching>
<staticContent>
<clientCache cacheControlMode="DisableCache" />
</staticContent>
</system.webServer>
</configuration>
For more granular control, consider these additional methods:
// Programmatic solution in Global.asax
protected void Application_BeginRequest()
{
if (Request.Url.AbsolutePath.EndsWith(".js") ||
Request.Url.AbsolutePath.EndsWith(".css"))
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
}
}
- Kernel cache changes require IIS reset
- Test with Fiddler/Postman to verify headers
- Monitor performance impact when disabling caching
- Combine with proper ETag configuration if needed
When IIS 7 unexpectedly returns HTTP 304 (Not Modified) responses for static resources (JS, CSS, images) even when the client sends Cache-Control: no-cache
, this typically indicates kernel-level caching interference. The behavior persists because:
- IIS kernel cache (http.sys) may still serve cached responses
- ETag validations occur before client cache directives are processed
- Default caching profiles override certain client headers
IIS 7's kernel caching operates at a lower level than application caching. To verify if this is the culprit, run:
netsh http show cachestate
If you see your static files listed, the kernel cache is serving them regardless of client headers.
This web.config solution properly disables caching at all levels:
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="false">
<profiles>
<add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".png" policy="DisableCache" kernelCachePolicy="DisableCache" />
</profiles>
</caching>
<staticContent>
<clientCache cacheControlMode="DisableCache" />
<remove fileExtension=".js" />
<mimeMap fileExtension=".js" mimeType="application/javascript" />
</staticContent>
</system.webServer>
</configuration>
For stubborn cases, combine these approaches:
- Disable dynamic content compression temporarily
- Clear the kernel cache:
net stop http /y
followed bynet start http
- Add unique query strings to resource URLs during development
For dynamic handling in ASP.NET applications:
protected void Application_PreSendRequestHeaders()
{
if (Response.ContentType == "application/javascript" ||
Response.ContentType == "text/css")
{
Response.Cache.SetNoStore();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
}
}