CORS 405 Method Not Allowed

Kevin Brydon picture Kevin Brydon · Mar 23, 2013 · Viewed 18.1k times · Source

I have been following the Mozilla article on how to set up my website to allow Cross-site scripting requests. Using IIS Manager I have added the following HTTP Response Headers

Access-Control-Allow-Origin  : *
Access-Control-Allow-Headers : Origin, SecurityPrivateKeyID
Access-Control-Allow-Methods : GET, POST, PUT, DELETE, OPTIONS

Despite this I keep getting a 405 Method Not Allowed when my browsers (Firefox and Chrome) send the pre-flight request with a custom SecurityPrivateKeyID header.

Request

OPTIONS /Service/Json/User.svc/ HTTP/1.1
Host: serviceprovider.com
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://client.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: securityprivatekeyid
Connection: keep-alive

Response

HTTP/1.1 405 Method Not Allowed
Allow: GET
Content-Length: 1565
Content-Type: text/html; charset=UTF-8
Server: Microsoft-IIS/8.0
access-control-allow-origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, SecurityPrivateKeyID
Date: Sat, 23 Mar 2013 08:35:03 GMT

The service works fine when accessing directly at http://serviceprovider.com/Service/Json/User.svc/.

Any ideas on what I am doing wrong?

[note I have changed my hosts files to point client.com and serviceprovider.com at my machine]

[a solution using JSONP will not do as my web service must be able to consume POST, PUT and DELETE methos]

Answer

Kevin Brydon picture Kevin Brydon · Mar 24, 2013

My IIS 8 instance is fresh installation, it seems I needed to make some modifications to the Handler Mappings

Backup IIS Configuration

In the event that any of the sugggested changes break your existing websites it's best to make a backup of the applicationhost.config file

  1. Navigate to C:\Windows\System32\inetsrv\config
  2. Make a copy of applicationhost.config

Remove Unused Handlers

As a starting point I removed all unused Handler Mappings to reduce the problem space. You can do this by modifying the applicationhost.config directly or by using IIS Manager

  1. Open IIS Manager
  2. Either on the server node or the individual website node select the Handler Mappings feature
  3. Manually remove all mappings that you don't need.

My websites are heavily service based and just depend on static files and files with the .aspx and .svc files extensions. I also manually removed all references to .NET 2.0 throughout the configuration file.

Add OPTIONS Handler

This seems to be the fix.

  1. Open IIS Manager
  2. Either on the server node or the individual website node select the Handler Mappings feature
  3. In the lefthand column select Add Module Mapping
  4. In the Add Module Mapping dialog use the following values.
    • Request path - *
    • Module - ProtocolSupportModule
    • Executable - [Leave blank]
    • Name - [Whatever you want]
  5. Click Request Restrictions
    • In the Mapping tab, unckeck Invoke handler only if request is mapped to
    • In the Verbs tab ensure OPTIONS is selected
    • In the Access tab select Script

My resulting Handlers configuration looks like this

<handlers accessPolicy="Read, Script">
    <add name="OPTIONS" path="*" verb="OPTIONS" modules="ProtocolSupportModule" resourceType="Unspecified" />
    <add name="svc-Integrated-4.0" path="*.svc" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
    <add name="SecurityCertificate" path="*.cer" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="File" />
    <add name="ISAPI-dll" path="*.dll" verb="*" modules="IsapiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" />
    <add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
    <add name="CGI-exe" path="*.exe" verb="*" modules="CgiModule" resourceType="File" requireAccess="Execute" allowPathInfo="true" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
    <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
</handlers>