Internet Information Services (IIS) 7 and later versions provide multiple approaches for configuring CORS. This guide covers modern, secure CORS implementations using web.config and the IIS CORS Module.
Access-Control-Allow-Origin: * allows any website to access your resources. Always specify exact origins in production.
IIS Version Compatibility:
For single trusted origin with proper OPTIONS handling:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<!-- SECURE: Specify allowed origin instead of wildcard -->
<add name="Access-Control-Allow-Origin" value="https://example.com" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type, Authorization" />
<add name="Access-Control-Max-Age" value="86400" />
<add name="Vary" value="Origin" />
</customHeaders>
</httpProtocol>
<!-- Handle OPTIONS preflight requests -->
<handlers>
<add name="OptionsHandler" verb="OPTIONS" path="*"
type="System.Web.DefaultHttpHandler"
resourceType="Unspecified"
requireAccess="None" />
</handlers>
<!-- Return 204 for OPTIONS requests -->
<rewrite>
<rules>
<rule name="CORS Preflight" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_METHOD}" pattern="^OPTIONS$" />
</conditions>
<action type="CustomResponse" statusCode="204" statusReason="No Content" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Note: The URL Rewrite Module must be installed for preflight handling. Available from Microsoft IIS Downloads or via Web Platform Installer.
When using credentials (cookies, HTTP authentication), you MUST specify an exact origin:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<!-- MUST use specific origin with credentials -->
<add name="Access-Control-Allow-Origin" value="https://example.com" />
<add name="Access-Control-Allow-Credentials" value="true" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type, Authorization" />
<add name="Access-Control-Max-Age" value="86400" />
<add name="Vary" value="Origin" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Important: This combination is INVALID per CORS specification:
<!-- INVALID - Will fail in browsers --> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Credentials" value="true" />
For sophisticated origin validation and multiple allowed origins, use the official IIS CORS Module (IIS 8.5+):
Install via Web Platform Installer or download from Microsoft IIS Extensions.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="true">
<add origin="https://example.com"
allowed="true"
allowCredentials="true"
maxAge="86400">
<allowHeaders>
<add header="Content-Type" />
<add header="Authorization" />
</allowHeaders>
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="PUT" />
<add method="DELETE" />
</allowMethods>
<exposeHeaders>
<add header="Content-Length" />
<add header="X-Custom-Header" />
</exposeHeaders>
</add>
<add origin="https://app.example.com" allowed="true" />
</cors>
</system.webServer>
</configuration>
Important: Application-level CORS (in ASP.NET code) takes precedence over web.config headers.
Best Practice:
For truly public APIs that don't use credentials:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<!-- WARNING: Only use for public APIs without credentials -->
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
If you don't have a web.config file already, create a new file called web.config at the root of your application or site containing one of the examples above.
Solution:
Solution: Don't set CORS headers in both web.config and application code. Choose one approach.
Solution: Replace wildcard with specific origin when using credentials:
<!-- Change from: --> <add name="Access-Control-Allow-Origin" value="*" /> <!-- To: --> <add name="Access-Control-Allow-Origin" value="https://example.com" />
Solution: Add the required method to Access-Control-Allow-Methods:
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
The Vary: Origin header prevents cache poisoning when CORS headers change based on the origin. Without it, one origin might receive another origin's cached response.
Always include when:
Access-Control-Max-Age to reduce preflight requests (86400 = 24 hours)For comprehensive testing instructions including curl commands, browser DevTools usage, and troubleshooting common CORS errors, see the CORS Testing Guide.
The content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.
Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain