IIS 回應 OPTIONS 404:從 CORS 預檢請求找到正確處理點

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 的處理鏈

  1. 應用程式路由:確認 API 或 SignalR endpoint 是否真的接受 OPTIONS,或是否由 CORS middleware 在進入路由前回應。
  2. IIS handler:若 IIS 的 OPTIONSVerbHandler 先攔截請求,才評估是否在該站台的 web.config 移除它。這是針對已確認的衝突做的設定,不應複製到所有網站。
  3. UrlScan/Request Filtering:舊站可能在 AllowVerbs 或 DenyVerbs 排除了 OPTIONS。若有 UrlScan,檢查實際生效的設定檔與 IIS log,而不是只改專案內的 web.config。
  4. 驗證與反向代理: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 運作方式與常見設定誤解 一起閱讀。

參考資料

Author image
關於 Richard Zheng
About me 喜歡爬山,瑜伽,溜冰,喜歡新奇的事,最喜歡的還是寫程式帶來的成就感,對於資訊會不斷的出現新事物也能抱持好奇與熱忱。近期開始將學習的心得寫在Blog,發現思路更清晰也加深了記憶。 紙上得來終覺淺,絕知此事要躬行