IIS 7은 200 대신 304를 반환합니다.


10

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

여기에 무엇이 잘못 될 수 있는지에 대한 단서가 있습니까?

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>

정확히 같은 문제가 발생하여 매우 바쁜 웹 서버에서 많은 문제가 발생합니다. 클라이언트에 아직 리소스 복사본이 없더라도 IIS는 304를 반환합니다.
Philippe Leybaert

@Philippe, 아직 해결책을 찾았습니까? 그렇지 않으면 위의 편집 방법을 확인하고 아래의 새 답변을 확인하십시오.
Ola Herrdahl 2016 년

@ OlaHerrdahl, 귀하의 해결 방법은 완벽합니다 :)이 정확한 문제도있었습니다. 감사!
Ardee Aram

답변:


3

에 따르면 HTTP1.1 사양의 14.9 절no-cache캐시-Control 헤더 지시자는 IIS가 요청의 헤더를 무시 의미 원본 서버에 의해에만 imposable입니다.

캐시 제어 지시문은 다음과 같은 일반적인 범주로 분류 할 수 있습니다.

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

오리진 서버에 의해.

14.9.1 절은 public, privateno-cache을 캐시 가능 대상을 제한하는 지시문으로 정의 하며 , 이는 서버에서만 부과 할 수 있습니다.

.js 파일을 캐시하지 않으려면 no-cache앱 에서 지시문 을 설정하거나 (예 : ASP.NET 코드) 지시문 Cache-Control을 사용하도록 요청 에서 헤더 를 변경해야합니다. no-store대신에 no-cache.

편집 :
귀하의 의견에 따라-예 파일을 캐시하고 싶지 않다고 가정했습니다. 그러면 파일이 IIS의 내부 캐시 중 하나에있는 결과로 304가 나올 수 있습니다. 이것들을 살펴보십시오.


나는 당신이 내 문제를 여기에서 오해했다고 생각합니다. 파일이 아직 캐시에 없습니다. 그러나 여전히 서버는 304로 응답합니다 ... 이것은 모든 주요 브라우저에서 사이트를 탐색 할 때 무작위로 발생하는 것 같습니다.
Ola Herrdahl

@Ola Herrdahl : 편집 내용을 살펴보십시오.
squillman

IIS에서도 캐싱을 비활성화하려고 시도했지만 아무런 차이가 없습니다 ... :(
Ola Herrdahl

1

잠시 동안 같은 문제가 발생하여 모든 캐싱이 해제되었습니다 ... 그러나 기본적으로 기존 사이트에서 정적 파일을 압축 할 수있는 IIS7 용 압축 모듈을 설치했습니다. 영향을받은 사이트에 대해 모든 압축을 해제했으며 이제는 미세한 나무를 사용하는 것 같습니다 .


1

또한이 버그가 발생했지만 자산 관리 라이브러리 (카세트)를 사용하고있었습니다. 이 문제를 광범위하게 조사한 결과이 문제의 근본 원인은 ASP.NET, IIS 및 Cassette의 조합임을 알 수 있습니다. 이것이 귀하의 문제 인지 확실하지 않지만 ( HeadersAPI 대신 CacheAPI 사용) 패턴이 동일한 것 같습니다.

버그 # 1

Cassette는 Vary: Accept-Encodinggzip / deflate로 컨텐츠를 인코딩 할 수 있으므로 헤더에 대한 응답의 일부로 헤더를 설정합니다 .

그러나 ASP.NET 출력 캐시는 항상 처음 캐시 된 응답을 반환합니다. 예를 들어, 첫 번째 요청에 Accept-Encoding: gzipCassette가 gzipped 컨텐츠를 리턴하면 ASP.NET 출력 캐시는 URL을로 캐시합니다 Content-Encoding: gzip. 동일한 URL에 대한 다른 허용 가능한 인코딩 (예 :)을 가진 다음 요청 Accept-Encoding: deflate은로 캐시 된 응답을 반환합니다 Content-Encoding: gzip.

이 버그는 Cassette가 HttpResponseBase.CacheAPI를 사용하여 출력 캐시 설정 (예 :)을 설정 Cache-Control: public하지만 HttpResponseBase.HeadersAPI를 사용하여 Vary: Accept-Encoding헤더 를 설정함으로써 발생합니다 . 문제는 ASP.NET OutputCacheModule이 응답 헤더를 인식 하지 못한다는 것입니다. CacheAPI 를 통해서만 작동 합니다. 즉, 개발자는 표준 HTTP가 아닌 보이지 않는 밀접하게 결합 된 API를 사용해야합니다.

버그 # 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-Match304 응답을 반환합니다. 예상치 못한 Accept-Encoding및을 (를) 무작위 요청으로 인해 모든 사용자에게 핵심 스크립트에 대해 304가 제공되는이 버그의 치명적인 영향을 확인했습니다 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 커널 / 사용자 모드 캐싱을 비활성화하는 것입니다. 이를 통해 웹 서버 앞에 추가 캐싱 계층을 배포 할 때 버그 1을 발견했습니다. 쿼리 문자열 해킹이 작동 한 이유 OutputCacheModuleCacheAPI가에 따라 달라지는 데 사용되지 않은 경우 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()
    {

    }
}

나는 이것이 누군가 😄 도움이되기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.