How to Configure IIS CORS Headers on Windows Server 2022
Cross-Origin Resource Sharing (CORS) is a browser security mechanism that restricts web pages from making requests to a different domain than the one that served the page. When you host an API or web service on IIS and your frontend application is served from a different origin, you must explicitly configure CORS headers so the browser allows the cross-origin request. This guide covers CORS configuration in IIS on Windows Server 2022 using web.config, the IIS CORS module, and PowerShell.
How CORS Works
When a browser makes a cross-origin request, it includes an Origin header in the request. The server must respond with an Access-Control-Allow-Origin header that either echoes the requesting origin or contains a wildcard (*). If this header is missing or does not match the request origin, the browser blocks the response—even if the server returned a 200 OK.
For requests that use non-simple methods (PUT, DELETE, PATCH) or custom headers, the browser first sends a preflight request using the HTTP OPTIONS method. The server must respond to this preflight with Access-Control-Allow-Methods and Access-Control-Allow-Headers before the browser will send the actual request. Misconfiguring or ignoring OPTIONS preflight is the most common CORS mistake in IIS deployments.
Adding CORS Headers via web.config
The simplest way to add CORS headers to an IIS site is through the system.webServer/httpProtocol/customHeaders section in web.config. This approach adds the headers to every response regardless of the requesting origin.
The Access-Control-Max-Age header tells the browser how long (in seconds) to cache the preflight response. Setting it to 86400 (24 hours) reduces the number of OPTIONS requests the browser sends. This is critical for performance in high-traffic APIs.
To allow any origin (public APIs with no credential requirements), replace the specific origin with a wildcard. Note that wildcards cannot be used alongside Access-Control-Allow-Credentials: true—you must echo the specific origin dynamically if credentials are required.
Handling OPTIONS Preflight Requests
The customHeaders approach alone is not sufficient for preflight requests. IIS will process an OPTIONS request through the normal handler pipeline. Depending on your application, OPTIONS may return a 405 Method Not Allowed or trigger authentication challenges before the CORS headers are evaluated. You must explicitly handle OPTIONS requests.
Add a URL rewrite rule or use the OPTIONSVerbHandler trick to return a 200 OK directly for OPTIONS requests without routing them to your application:
If Windows Authentication is enabled on the site, OPTIONS requests will receive a 401 challenge before the CORS headers are sent, which breaks preflight entirely. Disable authentication for OPTIONS requests using a rewrite condition or configure IIS to skip authentication for that verb.
Installing the IIS CORS Module
Microsoft released a dedicated IIS CORS Module (version 1.0) that provides native, configurable CORS support including dynamic origin matching, per-route rules, and wildcard subdomain support. Download it from the IIS downloads page or install it via Web Platform Installer.
# Download the IIS CORS module MSI (x64)
# https://www.iis.net/downloads/microsoft/iis-cors-module
# Silent install from command line
msiexec /i iis-corsmodule-amd64.msi /quiet /norestart
# Verify the module is registered
%windir%system32inetsrvappcmd.exe list module corspolicy*
After installing the module, configure it in web.config using the cors configuration section instead of customHeaders. The IIS CORS module approach is significantly more flexible than static headers.
Configuring Allowed Origins
The IIS CORS module supports exact origin matching, wildcard matching, and the special * wildcard for public APIs. When failUnlistedOrigins="true", requests from origins not listed in the configuration will receive a 403 response instead of the CORS headers being omitted silently.
Credentials Mode
When a browser sends a cross-origin request with credentials (cookies, HTTP authentication, or client-side certificates), it sets the withCredentials flag. The server must respond with Access-Control-Allow-Credentials: true AND specify an exact origin (not a wildcard). If either condition is not met, the browser rejects the response.
# Correct: exact origin + credentials allowed
# WRONG - this will NOT work with credentials
In the manual web.config approach (without the IIS CORS module), you cannot dynamically set the Access-Control-Allow-Origin to the value of the incoming Origin header without using URL Rewrite rules or application-level code. The IIS CORS module handles this automatically.
Dynamic CORS Origins with URL Rewrite
If you are not using the IIS CORS module but need to support multiple specific origins dynamically, use URL Rewrite to echo the Origin header back in the response when it matches an allowed list:
Testing CORS with curl
Use curl to simulate CORS requests from the command line. Add the Origin header manually to test how IIS responds:
# Test a simple GET request with an Origin header
curl -i -H "Origin: https://app.example.com" https://api.yourserver.com/endpoint
# Test an OPTIONS preflight request
curl -i -X OPTIONS https://api.yourserver.com/endpoint
-H "Origin: https://app.example.com"
-H "Access-Control-Request-Method: POST"
-H "Access-Control-Request-Headers: Content-Type, Authorization"
# Check the response headers for:
# Access-Control-Allow-Origin
# Access-Control-Allow-Methods
# Access-Control-Allow-Headers
# Access-Control-Allow-Credentials
Verifying in Browser DevTools
In Chrome or Edge, open DevTools (F12), go to the Network tab, and find the failing request. Click on it and look at the Response Headers section. If Access-Control-Allow-Origin is missing or does not match the page’s origin, the browser will show a CORS error in the Console tab with a message like: “Access to fetch at ‘https://api.yourserver.com’ from origin ‘https://app.example.com’ has been blocked by CORS policy.”
For preflight failures, look for the OPTIONS request in the Network tab. A 401, 403, or 405 response for the OPTIONS request is the most common cause of CORS failures on IIS when Windows Authentication is enabled.
Common CORS Mistakes on Windows/IIS
The most frequent CORS mistakes in IIS deployments include: setting CORS headers in customHeaders while also having the IIS CORS module installed (double headers cause browser rejection), enabling Windows Authentication without allowing anonymous access to OPTIONS requests, including a trailing slash in the allowed origin value (https://app.example.com/ does not match https://app.example.com), and setting Access-Control-Allow-Origin: * on an endpoint that also sets Access-Control-Allow-Credentials: true.
# Check for duplicate CORS headers in the response (common mistake)
curl -si -H "Origin: https://app.example.com" https://api.yourserver.com/ | grep -i "access-control"
# If you see two Access-Control-Allow-Origin lines,
# you have both customHeaders and the CORS module active.
# Remove one of them.
# Verify IIS authentication settings on OPTIONS verb
%windir%system32inetsrvappcmd.exe list config "Default Web Site/api" /section:system.webServer/security/authentication
Always test CORS from the actual browser, not just with curl. The browser applies additional restrictions such as checking that the response content type is correct for credentialed requests and that the exposed headers list does not include disallowed headers. Server-side testing with curl confirms the headers are present but does not replicate the full browser security model.