customFields에 의해 IIS Log의 httpErrors 응답코드가 바뀌는 현상

이 글은 Windows Server 2016, IIS 10.0.14393 에서 발생하는 현상을 담고 있다. 그 외 버전에서의 동작은 확인되지 않았다. 이 현상이 버그인지, 의도된 동작인지는 확인되지 않았다.

IIS에서는 보통 사용자에게 표시되는 오류페이지를 설정하기 위해 <httpErrors> 항목을 사용한다. 이 때 대부분 아래처럼 responseMode로 ExecuteURL을 사용한다.

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/error.htm" responseMode="ExecuteURL" />
    </httpErrors>
</system.webServer>

위처럼 설정하고 어떻게 동작하는지 확인하기 위해 없는 파일인 testfile.htm을 호출하면 사용자와 IIS Log 모두에 응답코드는 200으로 남는다. 이는 ExecuteURL이 동작하는 방식이다.

만약 사용자에게는 오류코드를 보여주고 싶지 않지만, 서버의 IIS Log에는 오류코드가 그대로 남는걸 원한다면, 아래처럼 ExecuteURL 대신 Redirect를 사용하면 된다.

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/error.htm" responseMode="Redirect" />
    </httpErrors>
</system.webServer>

위처럼 설정하고 testfile.htm을 호출하면 사용자에게는 요청한 testfile.htm에 대해선 302가 응답되고, 302로 인해 추가적으로 error.htm이 호출되며 200이 응답된다. IIS Log에는 testfile.htm 호출의 응답코드로 404가 기록된다. 추가적으로 호출된 error.htm의 응답코드는 200이 남는다. 이 동작에 대해선 Microsoft IIS Tech Blog의 Issue: IIS logs 200 status code instead of 404 글에도 잘 설명되어 있다. 여기까지는 원래 이렇게 동작하는 것이며, 문제가 없다.

문제는 Redirect 설정이 applicationHost.config의 <logFile><customFields>와 조합되었을 때 발생한다. 이 설정은 IIS Log에 헤더나 서버변수 값을 남기고 싶을 때 사용하는데, 상세한 설정 방법은 Microsoft 공식 문서 Adding Default Custom Fields to a Log File <add>를 참조하면 된다.

예를 들어, 만약 IIS Log에 요청헤더의 X-Forwarded-For 값을 남기고 싶다면, applicationHost.config에 아래처럼 설정하면 된다.

<logFile logFormat="W3C" directory="%SystemDrive%\inetpub\logs\LogFiles">
    <customFields>
        <add logFieldName="custom-header-xforwardedfor" sourceName="X-Forwarded-For" sourceType="RequestHeader" />
    </customFields>
</logFile>

이렇게 <customFields>를 설정하는 순간 Redirect 설정이 IIS Log에 남기는 응답코드가 달라진다. <customFields>가 없을 때는 위에 적은 대로 에러 요청 값의 에러코드(404, 500 등)가 IIS Log에 그대로 남지만, <customFields>가 설정되어 있을 때는 302가 남는다. 즉, 사용자에게는 오류코드를 숨기지만, IIS Log에는 오류코드를 제대로 남기기 위해 Redirect를 사용할 때는 <customFields>를 사용하지 못하는 것이다.

Leave a Comment