IIS 7 返回 304 而不是 200

IIS 7 返回 304 而不是 200

我在使用 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-cacheCache-Control 标头的指令仅由源服务器强制执行,这意味着 IIS 将忽略您请求中的标头。

缓存控制指令可以分为以下几类:

  - Restrictions on what are cacheable; these may only be imposed

由原始服务器。

14.9.1 节将publicprivate和定义no-cache为限制可缓存内容的指令,这些指令只能由服务器施加。

如果您不希望 .js 文件被缓存,则您需要no-cache在应用程序中设置指令(即 ASP.NET 代码),或者您需要更改Cache-Control请求中的标头以使用no-store指令而不是no-cache

编辑:
根据您的评论 - 是的,我假设您不希望缓存该文件。那么,304 可能是由于该文件位于 IIS 的内部缓存之一中而导致的。看看这些:

答案2

一段时间以来,我一直遇到同样的问题,并且已关闭所有缓存……但是,我安装了 IIS7 的压缩模块,默认情况下,该模块已启用现有网站上的静态文件压缩。我关闭了受影响网站的所有压缩,现在它们似乎运行正常触摸木头

答案3

我们也遇到了这个错误,但我们使用的是资产管理库(Cassette)。经过对这个问题的广泛调查,我们发现这个问题的根本原因是 ASP.NET、IIS 和 Cassette 的组合。我不确定这是否你的问题(使用HeadersAPI 而不是CacheAPI),但模式似乎相同。

漏洞 # 1

Cassette 将Vary: Accept-Encoding标头设置为对包的响应的一部分,因为它可以使用 gzip/deflate 对内容进行编码:

但是,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.HeadersAPI 设置标头而导致的。问题是Vary: Accept-EncodingASP.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:

但是,一旦 IIS 的内核和用户模式处理响应,它们将看到 URL 的响应已更改,并且应该更新缓存。如果netsh http show cachestate再次检查 IIS 内核缓存,缓存的 200 响应将被 304 响应替换。对该包的所有后续请求(无论是Accept-Encoding和 )都将返回 304 响应。我们看到了这个错误的破坏性影响,由于一个随机请求具有意外的和 ,If-None-Match所有用户的核心脚本都收到 304 响应。Accept-EncodingIf-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 之所以有效,是因为OutputCacheModuleCache如果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()
    {

    }
}

我希望这可以帮助别人

相关内容