SignalR 或前端跨網域呼叫 API 時,瀏覽器可能先送出一個 OPTIONS 請求,例如 /negotiate?negotiateVersion=1。若這個預檢請求在 IIS 直接得到 404,後續的 GET 或 POST 還沒送出,就已經被瀏覽器擋住。這篇記錄的是排查 IIS 回應 OPTIONS 404 的順序,而不是把 CORS 標頭加上去就算完成。
先確認它是不是 CORS preflight
從瀏覽器 Network 面板或 IIS log 觀察方法、URL、Origin 與 Access-Control-Request-Method。具有後兩個標頭的 OPTIONS,通常是瀏覽器為跨來源請求發出的 preflight。可用下列指令重現最小請求:
curl -i -X OPTIONS 'https://api.example.com/negotiate' \
-H 'Origin: https://app.example.com' \
-H 'Access-Control-Request-Method: POST'
測試結果至少要同時看 HTTP 狀態與 CORS 回應標頭。只看到 200,但沒有允許該來源、方法或必要 request header,瀏覽器仍會拒絕後續請求。
排查 IIS 的處理鏈
- 應用程式路由:確認 API 或 SignalR endpoint 是否真的接受 OPTIONS,或是否由 CORS middleware 在進入路由前回應。
- IIS handler:若 IIS 的
OPTIONSVerbHandler先攔截請求,才評估是否在該站台的web.config移除它。這是針對已確認的衝突做的設定,不應複製到所有網站。 - UrlScan/Request Filtering:舊站可能在 AllowVerbs 或 DenyVerbs 排除了 OPTIONS。若有 UrlScan,檢查實際生效的設定檔與 IIS log,而不是只改專案內的
web.config。 - 驗證與反向代理:preflight 通常在真正帶憑證的 API 呼叫之前發生;若反向代理或驗證規則先回應 401/404,也要在那一層處理。
web.config 範例的適用範圍
原始問題中,確認是 OPTIONS handler 衝突後,可在目標站台加入以下設定:
<system.webServer>
<handlers>
<remove name="OPTIONSVerbHandler" />
</handlers>
</system.webServer>
這只處理 IIS 的其中一層,並不會自動產生正確的 CORS 政策。CORS 應列出實際允許的前端網域、HTTP 方法與 request header;若要帶 Cookie 或 Authorization,不能同時用萬用字元來源。把 Access-Control-Allow-Origin: * 當成通用解法,會讓站點的授權界線變得不清楚。
驗證要看瀏覽器的最終行為
修正後,除了 curl 外還要用原本的網頁情境測試:preflight 是否回應預期的來源與方法、SignalR negotiate 是否成功、含登入狀態的請求是否仍被正確驗證。這可以避免「OPTIONS 從 404 變 204,但實際應用仍無法連線」的假性修復。
同一個問題若出現在一般 Fetch 呼叫,也可搭配本站的 CORS 運作方式與常見設定誤解 一起閱讀。
