上週為了讓同事拉一份檔案,我在測試機上把 IIS 專案資料夾開了個進階共用。東西傳完後我把共用關掉,繼續回頭改別的東西。
十分鐘後測試站同事回訊息:整個網站 HTTP 401,連 /favicon.ico 都打不開。
我第一個念頭是「我剛剛根本沒動 web.config 啊?」然後花了半小時繞遠路——這篇就是想分享這個坑,因為症狀跟原因之間的距離遠到不合理。
症狀
- 瀏覽器吃到
401 Unauthorized - 連 CSS、圖片這類靜態檔都 401
- IIS 沒有動過站點設定
- 專案程式碼沒動過
- App Pool 跑得好好的,沒當機、沒停
- 重啟 IIS 沒用
翻開 IIS 記錄(C:\inetpub\logs\LogFiles\W3SVC...),關鍵那行:
1 | sc-status: 401 |
401.3 的官方說明是 「Access is denied due to an ACL set on the requested resource」——翻譯:這不是 IIS 驗證設定的問題,是 NTFS 層級擋下來的。
我第一個走錯的方向
本能反應是去翻 App Pool 的 Identity。一講到「IIS 權限」,多數人腦中先跳出來的就是 IIS AppPool\網站名稱 這個虛擬帳號。
我還真的去 D:\Sites\YilanSewearV3 右鍵按屬性確認 IIS AppPool\YilanSewearV3 是不是被動到——在啊,好端端地躺在 ACL 裡。
一開始就搞錯抓誰了。
真正的犯人:IUSR 從 ACL 裡消失了
重新看一次屬性頁,才發現 IUSR 那一列整排不見了。
這個帳號我平常從來沒特別記過,存在感為零——直到它消失的那一刻才想起來:**IIS 站台預設開著 Anonymous Authentication,而這個匿名驗證預設用來檢查 NTFS 權限的身份就是 IUSR**(這個預設值其實可以改成 App Pool Identity,但多數情況沒人會去動它)。
靜態檔都讀不到,網站還沒跑到動態那一層就 401 了,完全合理。
修復(一行搞定)
1 | icacls "D:\Sites\YilanSewearV3" /grant "IUSR:(OI)(CI)RX" /T |
參數意思:
/grant "IUSR:...":授權給 IUSR(OI)Object Inherit:子檔案繼承(CI)Container Inherit:子資料夾繼承RX:Read & Execute/T:套到所有現存子目錄
跑完立刻活過來。
取消共用的副作用:一個我找不到官方答案的現象
老實說這段是我第二天補功課也沒完全搞清楚的部分。進階共用在開啟時會動到 NTFS ACL 這件事有跡可循;但關閉共用那一刻 IUSR 為什麼會連帶被掃掉,我在 Microsoft 官方文件裡翻不到明確說明。
我能講的只有實測:在同一台機器上另開測試資料夾跑了兩次(加 IUSR → 開進階共用 → 關共用),IUSR 都被清掉。所以這段請當成可重現的現象,不是已被官方定義的固定行為。IUSR 跟 SMB 共用其實完全沒關係,卻在這個過程裡被無辜波及——反覆踩過幾次之後我決定乖乖記下來。如果你在其他 Windows 版本上看到不一樣的結果,歡迎留言補充。
一次講清楚兩個常被搞混的身份
這次踩坑的根因,是我一開始沒把 IIS 的兩個身份分清楚:
| 帳號 | 什麼時候出場 |
|---|---|
IUSR |
匿名驗證下,讀靜態檔案(HTML / CSS / JS / 圖片)的身份 |
IIS AppPool\你的網站名 |
執行動態程式碼(.NET、Node)、存資料庫、寫 log 用的身份 |
很多人(包括當下的我)會以為既然 App Pool 代表一個網站,那所有檔案 I/O 都會用 App Pool 的身份去檢查權限——實際上不是。更精準的說法是:請求仍是在該站台所屬 App Pool 的 worker process(w3wp.exe)裡處理,但在匿名驗證情境下,IIS 對檔案做 ACL 檢查時用的是匿名身份(預設就是 IUSR),而不是 process identity 本身。
這是 IIS 裡「process identity」跟「impersonated identity」分成兩條線看的典型例子——同一個 worker 跑,但對外存取資源時被「代換」成另一個身份。
所以下次遇到類似狀況,判準是:
- 純靜態資源 401 → 先懷疑
IUSR - 靜態檔正常、動態頁面讀不到 DB / 寫不了 log → 再去翻 App Pool
兩條線分開查,少繞很多路。
下次我會怎麼做
正式機上的 IIS 專案資料夾,我不會再隨手開共用了。要交一份檔案給人,用 robocopy 或直接打包成 zip 都比開共用乾淨——沒有副作用、沒有權限被動到。
真的非開不可,關掉共用之後我會直接習慣性跑一次:
1 | icacls "D:\Sites\專案名" | findstr "IUSR IIS_IUSRS" |
確認兩個帳號都還在再走人。這個檢查花三秒,比下次再卡在 sc-substatus 3 跟 App Pool 之間繞半小時划算太多。
