IIS hijacks CORS Preflight OPTIONS request

I am making a CORS POST request and setting the Content-Type header to json. This triggers a Preflight OPTIONS request to fire (this is good and expected)

This OPTIONS request is responded to with a 200 OK but this isn't coming from my WebAPI application.

I have a custom Message Handler in place and it never get's hit so the request is getting responded to by IIS prior to hitting ASP.NET it seems.

I have found several posts on the subject and they say the following

  1. Make sure WebDav is uninstalled / removed / disabled - DONE

  2. Make sure the OPTIONSVerbHandler is removed / changed to use aspnet_isapi.dll - TRIED BOTH

  3. Make sure the extensionlessURLHandler includes the OPTIONS verb - DONE

However, my options request is still getting hijacked. By that I mean, IIS responds with at 200 OK but isn't including an Access-Control-Allow-Origin header in the response. It isn't including this header because it is never getting to my WebAPI CORS code that would set this header.

The two best posts I could find that sound like my issue are

here: https://stackoverflow.com/questions/14897247/jquery-stuck-at-cors-preflight-and-iis-ghost-response

and here: http://brockallen.com/2012/10/18/cors-iis-and-webdav/

I have tried turning on Failed Request tracing (FERB) in IIS and set it to trace all 200 status codes. I don't ever see the options request being logged... Not sure if this means FERB doesn't track OPTIONS requests or if I need to change something in the FERB settings to make it track OPTIONS requests, Or if this is a clue to what my problem is?

This is ASP.NET WebAPI 2.0 running on IIS 7.5 (Also tested on IIS 8 and IISExpress with same results) Doesn't matter what browser (Chrome, FF, and IE all fail the same way)

I have tried everything I can find on the subject and still can't fix my problem.

Help me StackOverflow, you're my only hope.

Answers

Check if URLScan tool is installed on IIS. When so check following section:


;
; The verbs (aka HTTP methods) listed here are those commonly
; processed by a typical IIS server.
;
; Note that these entries are effective if "UseAllowVerbs=1"
; is set in the [Options] section above.
;

GET
HEAD
POST
OPTIONS
Posted on by Anonymous

This is what worked for me:

  <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
Posted on by Shiroy

In my case, I missed the Microsoft.WebApi.Cors package. Installed this package and configured it like so in the WebApiConfig class:

 public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.EnableCors(new EnableCorsAttribute("*","*","*"));
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

Please fine-tune this before using in production because you probably don't want to have wild-cards for everything

Posted on by Nico Timmerman

In our case it was request filtering in IIS disabling OPTIONS verb at the root web application level. Open up IIS Manager, click on root application, click on Request Filtering, if OPTIONS appears in list either remove or Allow Verb. Wish I had checked this first as lots of wasted time.

Posted on by Anonymous

that's what worked for me after 4 hours of searching/experimenting:

    <handlers>
        <remove name="OPTIONSVerbHandler" />
        <add name="OPTIONSVerbHandler" path="*" verb="OPTIONS" modules="IsapiModule" scriptProcessor="C:\Windows\System32\inetsrv\asp.dll" resourceType="Unspecified" requireAccess="None" />
    </handlers>
Posted on by Anatoly Alekseev

I tried all of the above suggestions as well as others I found on SO and what mattered in my situation was we had Request Filtering enabled on IIS and the OPTIONS HTTP Verb was not in the list of allowed verbs. Once I added it I was able to sort out the rest of it.

Posted on by Anonymous

I had the same issue and the following web.config settings fixed it for me.

    <modules runAllManagedModulesForAllRequests="false">
      <remove name="FormsAuthenticationModule" />
    </modules>
    <handlers>
      <remove name="OPTIONSVerbHandler" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>

I was then able to handle CORS OPTIONS requests manually in Application_BeginRequest.

I was originally using the library detailed in this blog post for handling CORS requests. The product I'm working on requires that runAllManagedModulesForAllRequests be set to false, though. This is why I had to set up a custom implementation, but if you don't have that requirement you should give that library a try. It worked great when I was able to have runAllManagedModulesForAllRequests set to true.

Posted on by jdehlin

A couple of things you can try here, all web.config related, firstly modify your modules element to include the attribute runAllManagedModulesForAllRequests="true", as below:

<modules runAllManagedModulesForAllRequests="true">
	<remove name="WebDavModule" />
</modules>

Then set your handlers to the below:

<handlers>
   <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
   <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
   <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
   <remove name="WebDav" />
   <remove name="OPTIONSVerbHandler" />
   <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
   <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
   <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

This should do the trick, but if it doesn't, as a last resort you can force IIS to output the correct headers with the below:

  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>

Be wary of the wildcard value, you should really set this to the domain name that your site will be hosted on.

Posted on by Tom Hall