Назад | Перейти на главную страницу

IIS 7 возвращает 304 вместо 200

У меня странная проблема с IIS 7.
Иногда кажется, что вместо 200 возвращается 304.

Вот пример запроса, записанного с помощью 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

Кто-нибудь знает, что здесь может быть не так?

Я использую IIS 7 на Windows Web Server 2008 R2.

РЕДАКТИРОВАТЬ:

Я нашел обходной путь: включить кеширование, а затем отключить его на уровне расширения, помогло мне.

<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>

В соответствии с раздел 14.9 спецификации HTTP1.1, то 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. Взгляните на это:

У нас тоже была эта ошибка, но мы использовали библиотеку управления активами (Cassette). После тщательного расследования этой проблемы мы обнаружили, что основная причина этой проблемы связана с комбинацией ASP.NET, IIS и Cassette. Я не уверен, что это ваш проблема (использование Headers API, а не Cache API), но картина, похоже, такая же.

Ошибка №1

Кассета устанавливает Vary: Accept-Encoding заголовок как часть ответа на пакет, поскольку он может кодировать содержимое с помощью gzip / deflate:

Однако кэш вывода ASP.NET всегда будет возвращать ответ, который был кэширован первым. Например, если в первом запросе Accept-Encoding: gzip и Cassette возвращает сжатое содержимое, кэш вывода ASP.NET будет кэшировать URL как Content-Encoding: gzip. Следующий запрос к тому же URL-адресу, но с другой допустимой кодировкой (например, Accept-Encoding: deflate) вернет кешированный ответ с Content-Encoding: gzip.

Эта ошибка вызвана тем, что кассета использует HttpResponseBase.Cache API для настройки параметров кэша вывода (например, Cache-Control: public) но используя HttpResponseBase.Headers API для установки Vary: Accept-Encoding заголовок. Проблема в том, что ASP.NET OutputCacheModule является не осведомлены о заголовках ответов; он работает только через Cache API. То есть он ожидает, что разработчик будет использовать невидимо тесно связанный 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-адреса изменился и кеш должен быть обновлен. Если кеш ядра IIS проверяется с помощью netsh http show cachestate снова кэшированный ответ 200 заменяется ответом 304. Все последующие запросы к пакету, независимо от Accept-Encoding и If-None-Match вернет ответ 304. Мы увидели разрушительные последствия этой ошибки, когда всем пользователям было предоставлено 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 все еще воспроизводилась.

Нашим первоначальным исправлением этой ошибки было отключение кэширования в режиме ядра / пользователя IIS только для запросов Cassette, подобных другим, упомянутым. Таким образом, мы обнаружили ошибку №1 при развертывании дополнительного уровня кеширования перед нашими веб-серверами. Причина, по которой взлом строки запроса сработал, заключается в том, что OutputCacheModule будет записывать промах кеша, если Cache API не использовался для изменения в зависимости от QueryString и если в запросе есть QueryString.

Обходной путь

Мы в любом случае планировали отказаться от Cassette, поэтому вместо того, чтобы поддерживать нашу собственную вилку Cassette (или пытаться объединить PR), мы решили использовать HTTP-модуль для решения этой проблемы.

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()
    {

    }
}

Надеюсь, это кому-то поможет 😄!

У меня была такая же проблема какое-то время, и все кеширование отключено ... Однако в какой-то момент я установил модуль сжатия для IIS7, который по умолчанию включал сжатие статических файлов на моих существующих сайтах. Я отключил сжатие для затронутых сайтов, и теперь они, похоже, работают нормально постучать по дереву.