我在使用 IIS 7 时遇到了一个奇怪的问题。
有时它似乎返回 304 而不是 200。
这是使用 Fiddler 捕获的示例请求:(
请注意,请求的文件尚未位于我的浏览器缓存中。)
GET https://[mysite]/Content/js/jquery.form.js HTTP/1.1 Accept: */* Referer: https://[mysite]/Welcome/News Accept-Language: sv-SE User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: [mysite] Connection: Keep-Alive Cache-Control: no-cache Cookie: ...
请注意,请求中没有 If-Modified-Since 或 If-None-Match。
但响应仍然是:
HTTP/1.1 304 Not Modified Cache-Control: public Expires: Tue, 02 Mar 2010 06:26:08 GMT Last-Modified: Mon, 22 Feb 2010 21:58:44 GMT ETag: "1CAB40A337D4200" Server: Microsoft-IIS/7.5 X-Powered-By: ASP.NET Date: Mon, 01 Mar 2010 17:06:34 GMT
有谁知道这里可能出了什么问题?
我在 Windows Web Server 2008 R2 上运行 IIS 7。
编辑:
我找到了一种解决方法,启用缓存然后在扩展级别禁用它对我来说很有效。
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".png" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".gif" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
</profiles>
</caching>
<staticContent>
<clientCache cacheControlMode="NoControl" />
</staticContent>
</system.webServer>
</configuration>
答案1
根据HTTP1.1 规范第 14.9 节,no-cache
Cache-Control 标头的指令仅由源服务器强制执行,这意味着 IIS 将忽略您请求中的标头。
缓存控制指令可以分为以下几类:
- Restrictions on what are cacheable; these may only be imposed
由原始服务器。
14.9.1 节将public
、private
和定义no-cache
为限制可缓存内容的指令,这些指令只能由服务器施加。
如果您不希望 .js 文件被缓存,则您需要no-cache
在应用程序中设置指令(即 ASP.NET 代码),或者您需要更改Cache-Control
请求中的标头以使用no-store
指令而不是no-cache
。
编辑:
根据您的评论 - 是的,我假设您不希望缓存该文件。那么,304 可能是由于该文件位于 IIS 的内部缓存之一中而导致的。看看这些:
答案2
一段时间以来,我一直遇到同样的问题,并且已关闭所有缓存……但是,我安装了 IIS7 的压缩模块,默认情况下,该模块已启用现有网站上的静态文件压缩。我关闭了受影响网站的所有压缩,现在它们似乎运行正常触摸木头。
答案3
我们也遇到了这个错误,但我们使用的是资产管理库(Cassette)。经过对这个问题的广泛调查,我们发现这个问题的根本原因是 ASP.NET、IIS 和 Cassette 的组合。我不确定这是否你的问题(使用Headers
API 而不是Cache
API),但模式似乎相同。
漏洞 # 1
Cassette 将Vary: Accept-Encoding
标头设置为对包的响应的一部分,因为它可以使用 gzip/deflate 对内容进行编码:
- BundleRequestHandler(捆绑请求处理程序):https://github.com/andrewdavey/cassette/blob/a2d9870eb0dc3585ef3dd542287595bc6162a21a/src/Cassette.Aspnet/BundleRequestHandler.cs#L78
- HttpResponse实用程序:https://github.com/andrewdavey/cassette/blob/a2d9870eb0dc3585ef3dd542287595bc6162a21a/src/Cassette.Aspnet/HttpResponseUtil.cs#L45
但是,ASP.NET 输出缓存将始终返回第一个缓存的响应。例如,如果第一个请求具有Accept-Encoding: gzip
并且 Cassette 返回经过 gzip 压缩的内容,则 ASP.NET 输出缓存将把 URL 缓存为Content-Encoding: gzip
。对同一 URL 但具有不同可接受编码(例如 )的下一个请求Accept-Encoding: deflate
将返回具有 的缓存响应Content-Encoding: gzip
。
此错误是由于 Cassette 使用 APIHttpResponseBase.Cache
设置输出缓存设置(例如Cache-Control: public
),但使用HttpResponseBase.Headers
API 设置标头而导致的。问题是Vary: Accept-Encoding
ASP.NETOutputCacheModule
不是了解响应标头;它仅通过 API 工作Cache
。也就是说,它要求开发人员使用隐形紧密耦合的 API,而不仅仅是标准 HTTP。
漏洞 # 2
使用 IIS 7.5 (Windows Server 2008 R2) 时,错误 # 1 可能导致 IIS 内核和用户缓存出现单独的问题。例如,一旦使用 成功缓存了某个包Content-Encoding: gzip
,就有可能使用 看到它在 IIS 内核缓存中netsh http show cachestate
。它显示带有 200 状态代码和“gzip”内容编码的响应。如果下一个请求具有不同的可接受编码(例如
Accept-Encoding: deflate
)和如果标头If-None-Match
与包的哈希值匹配,则进入 IIS 内核和用户模式缓存的请求将被视为错过. 因此,导致请求由 Cassette 处理并返回 304:
- BundleRequestHandler(捆绑请求处理程序):https://github.com/andrewdavey/cassette/blob/a2d9870eb0dc3585ef3dd542287595bc6162a21a/src/Cassette.Aspnet/BundleRequestHandler.cs#L44
但是,一旦 IIS 的内核和用户模式处理响应,它们将看到 URL 的响应已更改,并且应该更新缓存。如果netsh http show cachestate
再次检查 IIS 内核缓存,缓存的 200 响应将被 304 响应替换。对该包的所有后续请求(无论是Accept-Encoding
和 )都将返回 304 响应。我们看到了这个错误的破坏性影响,由于一个随机请求具有意外的和 ,If-None-Match
所有用户的核心脚本都收到 304 响应。Accept-Encoding
If-None-Match
问题似乎是 IIS 内核和用户模式缓存无法根据标Accept-Encoding
头而变化。作为证据,通过使用Cache
以下解决方法的 API,IIS 内核和用户模式缓存似乎总是被跳过(仅使用 ASP.NET 输出缓存)。可以通过使用netsh http show cachestate
以下解决方法检查是否为空来确认这一点。ASP.NET 直接与 IIS 工作程序通信,以根据每个请求有选择地启用或禁用 IIS 内核和用户模式缓存。
我们无法在较新版本的 IIS(例如 IIS Express 10)上重现此错误。但是,错误 # 1 仍然可以重现。
我们最初修复此错误的方法是仅针对 Cassette 请求禁用 IIS 内核/用户模式缓存,就像其他人提到的那样。通过这样做,我们在 Web 服务器前面部署了额外的缓存层时发现了错误 # 1。查询字符串 hack 之所以有效,是因为OutputCacheModule
Cache
如果API 尚未使用,则会记录缓存未命中,具体取决于QueryString
和如果请求包含QueryString
。
解决方法
我们已经计划放弃 Cassette,因此,我们选择使用 HTTP 模块来解决这个问题,而不是维护我们自己的 Cassette 分支(或尝试合并 PR)。
public class FixCassetteContentEncodingOutputCacheBugModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += Context_PostRequestHandlerExecute;
}
private void Context_PostRequestHandlerExecute(object sender, EventArgs e)
{
var httpContext = HttpContext.Current;
if (httpContext == null)
{
return;
}
var request = httpContext.Request;
var response = httpContext.Response;
if (request.HttpMethod != "GET")
{
return;
}
var path = request.Path;
if (!path.StartsWith("/cassette.axd", StringComparison.InvariantCultureIgnoreCase))
{
return;
}
if (response.Headers["Vary"] == "Accept-Encoding")
{
httpContext.Response.Cache.VaryByHeaders.SetHeaders(new[] { "Accept-Encoding" });
}
}
public void Dispose()
{
}
}
我希望这可以帮助别人