みなさんこんにちは!ヒロポンです!
X見てるとマジで「IISデプロイで月曜朝に真っ白になった」って人多い!!!あーーみんなハマってるんだなーって。
publish後にプールリサイクルかけ忘れでアプリ更新したのに「あれ、全然反映されないんだけど?」ってなる、みたいな罠。
同業から「マネージドコードなしにしちゃうと暗号キーがレジストリに残らなくて、リサイクル後に認証通らなくなる」って聞いた時、あーー業務SEが1人で本番投入するとこういうのに普通に踏み抜くやろなーーと思った次第。。。。
ASP.NET MVC 5をIISに初めて載せた月曜朝7時、客先電話が鳴ります。
「真っ白だぞ。何これ。」
私もこれで一度アプリプールの.NET CLRバージョンv4.0にし忘れて、デプロイ後の月曜朝に全画面500.21で死んでた経験があります。
朝7時の客先電話、マジで嫌な記憶。
今回は連載 WinForms業務SEのためのASP.NET生存ガイドの連載です。
「2週間でASP.NETを最低限触れるようにする4ステップ」の①環境作るの到達点として、IISにアプリを載せる初手を5ステップで整理します。
💡 通奏低音:連載#1〜#7で立ててきた『HTMLレイヤーか、ASP.NETレイヤーか』の分離思考を、IISデプロイでは『IISレイヤーか、ASP.NETレイヤーか』に翻訳して切り分け軸にします。真っ白/ 500 / 404の症状を、まずどっちのレイヤーが主犯かを切り分けるだけで原因特定が半分終わる。
忙しいあなたのために!最初にまとめ!!!
- 5ステップ: ① VSのフォルダー発行で出力→ ② アプリプール作成(.NET CLR v4.0 +統合パイプライン)→ ③ 物理パス+仮想ディレクトリでIISに登録→ ④ web.config transformで本番接続文字列に切替→ ⑤ 動かない時の切り分け (症状別)
- ハマり真犯人はアプリプールの.NET CLRバージョンをv4.0にし忘れること。これだけで全画面500.21で死にます
- 切り分け軸: 真っ白/ 500.19・500.21 / 401・403はIISレイヤー、500内部サーバー/ 404はASP.NETレイヤー
- 今回の動作確認: IISはLinux containerでは検証不可なので、Windows + IIS 10 (Server 2019 or Windows 10)実機環境で実行してください
以上!!!!!
連載通奏低音の翻訳— IISレイヤーかASP.NETレイヤーか
連載#1 WinFormsのFormとRazor Viewの対応関係で書いた「HTMLレイヤーか、ASP.NETレイヤーか」の分離思考、IISデプロイでも同じ構造がマジで効きます。
これは関連する話なので、興味があるあなたは別タブで開いて後で呼んでくださいな。
デプロイ後にブラウザでアクセスして動かない場面に遭遇したら、まずどっちのレイヤーが主犯かを切り分ける。

- IISレイヤー (真っ白・500.19/500.21・401/403):アプリ自体が起動してないor権限不足
- ASP.NETレイヤー (500内部サーバー・404):アプリは起動したが中で例外orルーティング失敗
切り分けが先、ピンポイント修正は後。
これが朝7時の客先電話で慌てないコツ。
Step 1: Visual Studioのフォルダー発行で出力
業務SE 1人運用の現場ならフォルダー発行が一番枯れてて安全。
Web DeployはCI/CDで自動化する時に使う技で、デプロイ初回は手動コピーでIISの地形を理解する方が後々楽です。
Visual Studioで:
- ソリューションエクスプローラ→プロジェクト右クリック→ 「発行」
- ターゲットに「フォルダー」を選択
- 出力フォルダーパスを指定(例:
C:\publish\MyApp) - 「発行」ボタン
出力フォルダーの中身はこんな感じになります。
C:\publish\MyApp\
├── bin\ ← DLL 群
├── Content\ ← CSS / 画像
├── Scripts\ ← JS
├── Views\ ← Razor (.cshtml)
├── Global.asax
├── PrecompiledApp.config
└── Web.config ← 本番用 (transform 後)
これをそのままIISサーバーの物理パス先にコピーすればStep 1完了。
ZIPに固めて運用PCで展開、でもOKです。
Step 2:アプリケーションプールを作る(★真犯人ポイント)
ここが我々が一番踏む罠。
アプリプールの.NET CLRバージョンを間違えると、アプリが一切起動せず全画面HTTP Error 500.19 or 500.21で死にます。
IISマネージャーを開いて:
- アプリケーションプール →右クリック→ 「アプリケーションプールの追加」
- 名前:
MyAppPoolなど - .NET CLRバージョン:
.NET CLR バージョン v4.0.30319← ここがマジで真犯人ポイント - マネージパイプラインモード:
統合← MVC 5は統合パイプライン
5.「OK」
.NET CLR バージョン v2.0を選ぶと、.NET Framework 2.0/3.0/3.5アプリ用のランタイムなので、4.7.2ベースのMVC 5アプリは一切動きません。
ここからは現場メモ。
数年前、社内向け業務システムのASP.NET MVC 5化案件で「新IISサーバーにデプロイしたら全画面エラーが出る」という障害票が朝一で上がってきました。
リリース夜の私の最初の判断は「web.configの構文ミスっぽい」。
XML閉じ忘れか属性typoだと思った。
なのでweb.configを舐めるように再チェック。
「これでイケる、完了!よっしゃーー予定通り定時で帰れる!」ってなってました。
結果HTTP Error 500.21の黄色画面のまま、、、、ピクリとも動かない。
業務側からは「明日朝の始業までに使えるようにしろ」の連絡。
定時で帰る予定が。。。背中に冷や汗。
真夜中の3時から朝の6時まで、web.configと権限を疑って時間が溶けました。
慌ててアプリプールの詳細設定を開いたら.NET CLRバージョンがv2.0のまま。1クリックでv4.0に変えたら一発で起動。マジで救われた。
ギリギリ耐えた。(耐えてない)
今だったら500.21見た瞬間「あ、アプリプールCLRバージョンやん??」って思いますよ?
でもね。。当時は「web.configの中身か権限のどっちか」 という単純な式しか頭になかったんですよね。
対策はアプリプール作成時の初期設定でv4.0を選ぶクセを付ける。
既存プール(DefaultAppPool)を流用する時も、詳細設定で確認するのが安全です。
Step 3:物理パスと仮想ディレクトリ
IISにサイトを追加する画面で、物理パスとエイリアス (仮想ディレクトリ名)の意味が分かってないと、Step 1で配置したアプリに辿り着けません。
- 物理パス:サーバー上の実フォルダ(例:
C:\inetpub\wwwroot\MyApporC:\publish\MyApp) - 仮想ディレクトリ/エイリアス: URL上のパス(例:
http://server/myapp) - アプリケーション:物理パス+エイリアス+アプリプールの組み合わせ
IISマネージャーで:
- サイト →既存サイト(Default Web Site等)を右クリック→ 「アプリケーションの追加」
- エイリアス:
myapp(URLに出る名前) - アプリケーションプール: Step 2で作った
MyAppPoolを選択 - 物理パス: Step 1で配置した
C:\publish\MyApp
5.「OK」
ブラウザでhttp://localhost/myappにアクセス。
ここでStep 4の罠が出ます。
Step 4: web.configのtransformで本番接続文字列に切替
開発環境のWeb.configにはDev DBの接続文字列が書いてあるはず。
これを本番DBの接続文字列に書き換えないと、起動はするけどDBアクセスで全部例外、という事象になります。
Visual StudioのソリューションにはWeb.Release.configが自動で生成されてて、これがtransformファイル。
ビルド構成Releaseで発行すると、Web.configの特定要素をWeb.Release.configの指示で書き換える仕組み。
<!-- Web.Release.config -->
<connectionStrings>
<add name="MyDbConn"
connectionString="Server=PROD-SQL01;Database=MyAppProd;User Id=prod_user;Password=********;"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
</connectionStrings>
xdt:Transform="SetAttributes"で属性を書き換え、xdt:Locator="Match(name)"でname属性で対象要素を特定。
これでStep 1のフォルダー発行時に自動的に本番用Web.configが出力されます。
ここからまた現場メモから学ぶ罠。
別件の案件で、私はビルド構成Debugのまま発行ボタン押して、Dev DBの接続文字列のまま本番サーバーにデプロイしたことがあります。
IISでは普通に起動して画面も出る。。。。。けど、ボタン押した瞬間にSqlExceptionが噴き出す。
スタックトレースにDev DBのホスト名が露呈する事象を半日かけて追った。
技術的な復旧は接続文字列を直して再デプロイで30分なんですけど、業務側に「Dev DBの名前が見えてました」の説明に行った時の足の重さ。
信頼回復にもう半日。
後で気付いた時に「これWeb.Release.config書いてなかっただけじゃん」で頭抱えた。
これが罠の正体。
対策は発行プロファイル設定で「構成: Release」を選ぶ + transformの動作確認は事前にPreview Transformで確認すること。
これはVSの右クリックメニューから出せます。
Step 5:動かない時の症状切り分け(5症状マトリクス)
ここが今回の中核。
デプロイ後に動かない症状は5種類で、それぞれ主犯レイヤーと確認場所が違います。

💡上のPNGは視覚的な全体俯瞰用。検索エンジンにはalt文字列しか拾われないので、「HTTP Error 500.19」「HTTP Error 401.3」等の具体エラー文字列で検索した時の即時マッチ用として、同じ対応関係を下の表にも書いておきます。
| HTTPエラーコード/症状 | 主犯レイヤー | 確認場所 | 復旧の方向 |
|---|---|---|---|
| 真っ白画面(中身が出ない) | IIS / web.config構文エラー | イベントビューア→ Applicationログ | web.configのXML構文・customErrors設定 |
| HTTP Error 500.19 | IIS /アプリプール設定不可 | アプリプール→詳細設定→ .NET CLR | .NET CLR v4.0 +統合パイプラインに修正 |
| HTTP Error 500.21 | IIS / managedHandlerエラー | アプリプール→詳細設定→ .NET CLR | 同上(Step 2真犯人) |
| HTTP Error 500 (内部サーバー) | ASP.NET /例外 | View Source / customErrors=Off | 例外メッセージから原因特定 |
| HTTP Error 404 | ASP.NET /ルーティング | RouteConfig.cs / Global.asax | デフォルトルート登録・Areas確認 |
| HTTP Error 401.3 | IIS / NTFS権限 | フォルダ→セキュリティ→ IIS_IUSRS | フォルダNTFS読み取り権限を付与 |
| HTTP Error 403.14 | IIS /ディレクトリ参照禁止 | Default Document設定/ルート | デフォルトコンテンツ未配置・ルート未登録 |
要点を箇条書きで再整理:
- 真っ白画面 → web.config構文エラーor customErrors=Onで抑制
- HTTP Error 500.19 / 500.21 →アプリプールの.NET CLRがv4.0になってない(Step 2真犯人)
- HTTP Error 500 (内部サーバー) → ASP.NETレイヤーの例外。customErrors=Offで本物のエラーを出す
- HTTP Error 404 →ルーティング。RouteConfig.csとGlobal.asaxを確認
- HTTP Error 401.3 / 403.14 →アプリプールIDの権限不足orデフォルトコンテンツ未配置
症状から逆引きで切り分ける、が原則。
IISマネージャーの画面を闇雲に触る前に、まず症状の文字列を読む。
朝7時の客先電話を取った時、「ん??真っ白って言ってるからIISレイヤーか」と即判断できるかどうかで、その日の朝が決まります!!!
ハマりポイント—そうじゃないケースが3つあります
ここまで「5ステップ通せば動く」みたいに書いてきましたが、そうじゃないケースが3つあります。
欲張りなあなたのために、特別に共有しておきます。
①アプリプール.NET CLRをv4.0に直し忘れ(Step 2真犯人)
Step 2でアプリプール作る時、デフォルトのままOKしがち。
これがマジで罠で、デフォルトのCLRバージョンが現場のWindows Serverバージョンによってv2.0だったりv4.0だったりするんですよね。
v2.0のまま起動した瞬間にMVC 5アプリがなんと全画面HTTP Error 500.21で死にます。
私もこれで月曜朝7時に「真っ白だぞ」と客先電話を食らった。
30分プールの設定とにらめっこ。。。。背中に冷や汗。
これもやらかしの原因。
IISマネージャー→アプリプール詳細設定→ .NET CLRを1クリックでv4.0に変えるだけで直る話なのに、web.configと権限を疑って時間が溶けました。
対策はアプリプール作成時に「.NET CLRバージョン」をv4.0に明示 + マネージパイプラインモードを「統合」にする。既存プール流用時も詳細設定で確認する習慣にしておくと安全です。
②物理パスのNTFS権限不足で401/403
開発サーバーから本番サーバーに同じフォルダ名でコピーしたつもりで、NTFS権限を見てない。
これがやらかしのパターン。
ブラウザでアクセスするとHTTP Error 401.3 (Access Denied) or 403が出る。
アプリプールID (ApplicationPoolIdentity)がフォルダを読めない状態です。
後輩のデプロイ作業を引き取って、「なんでローカル開発は動くのに本番だけ401なんだ??」と詰まった時、ヤバいことに2時間権限の追跡に時間を使いました。
結局フォルダ右クリック→セキュリティ→ IIS_IUSRSを追加で解決。
経験ないですか??ありますよね。多分。
対策は、Step 1でコピーした物理パスフォルダに、IIS_IUSRSグループに対する読み取り権限を付与すること。
GUIなら「セキュリティ」タブ、PowerShellならicaclsで1行で済みます。
③ Web.Release.configのtransform失敗(Step 4)
ビルド構成Debugのまま発行したり、xdt:Transformの属性指定をtypoしたり。
よくあるやらかしです。
そうするとDevの接続文字列がそのまま本番にデプロイされる。
IISでは起動するんですけど、DBアクセスでSqlExceptionが出てスタックトレースにDev DBのホスト名が露呈する事象になります。
でもね。。気付いた瞬間、力が抜けるんですよ。
対策は、発行前にVSのWeb.Release.config右クリック→ Preview Transformでtransform後のWeb.configを確認すること。
もしくは発行プロファイル設定で「構成: Release」を選んでいるか確認する。
Visual Studioの発行ダイアログでConfigurationがDebugのままだとtransformが走らないんで、ここが地味な落とし穴。絶対にやらないで。。
まとめ
ASP.NET MVC 5をIISに載せる初手は5ステップ:
- VSのフォルダー発行で出力(ZIPコピー運用が枯れてて安全)
- アプリプール作成(.NET CLR v4.0 +統合パイプラインが真犯人ポイント)
- 物理パス+仮想ディレクトリでIISに登録
- Web.Release.configのtransformで本番接続文字列に切替
- 動かない時の症状切り分け(5症状→ IIS or ASP.NETレイヤー)
連載通奏低音の「IISレイヤーか、ASP.NETレイヤーか」で切り分けてからIISマネージャーを触ると、朝7時の客先電話の前に「あ、これアプリプールCLRバージョンだな」と即判断できるようになります!!!!!
ぶっちゃけ、IISデプロイは初回さえ通せば次からはこんな感じで手順をなぞるだけの作業に変わります。はい。
WinFormsですらインストーラー作る時にアプリプール(に相当するもの)に引っかかった人いるはずなので、心理的ハードルは思ってるほど高くないんですよね??
いい感じに地形を一度押さえれば、次の現場でも資産として使えます!!
動作確認メモ:今回の手順はWindows + IIS 10 (Server 2019 or Windows 10)+ .NET Framework 4.7.2 / MVC 5の実機環境を前提にしています。IISはLinux container上では動かないため、Docker検証は実施していません。Windows実機での手順なぞりが必要です。
よくある質問
Q1.アプリプールの.NET CLRバージョンをv2.0のままにしたらどうなりますか?
A. MVC 5アプリは全部HTTP Error 500.19か500.21で死にます。
デプロイ後にいきなり真っ白+黄色エラー画面になる典型パターン。
アプリプール詳細設定で.NET CLR v4.0 +統合パイプラインモードに変えるだけで直ります。
Step 2で詳しく書いたとおり。
Q2. IISにデプロイしたら真っ白画面で何も出ません
A. web.configのXML構文エラーか、customErrors=Onでエラー画面が抑制されている可能性が高いです。
イベントビューア→ Applicationログを最初に見るのが鉄則。
デバッグ用にcustomErrorsをOffにすると本物の例外メッセージが出ます。
Q3. Visual Studioのフォルダー発行とWeb Deployのどちらを使うべき?
A.業務SE 1人運用ならフォルダー発行→ ZIP →手動コピーが最も枯れていて安全です。
Web DeployはCI/CDで自動化する時に使う。
最初の1回はフォルダー発行でIISの地形を理解するのが推奨。
📚連載「WinForms業務SEのためのASP.NET生存ガイド」(全10回)の他の記事
- Part 1: WinFormsのFormとRazor Viewの対応関係を業務SEが一日で腹落ちさせる←既公開・HTML / ASP.NETレイヤー分離思考はこちら
- Part 2: ControllerはWinFormsのForm_Load拡張版だと理解する←既公開
- Part 3: ASP.NET MVC 5のルーティングをWinFormsのForm切替で理解する←既公開
- Part 4: ASP.NET MVC 5で使えるORM 3択— EF6 / Dapper / ADO.NETの業務SE視点比較←既公開・Step 4の接続文字列切替の前提はこちら
- Part 5: ASP.NET MVC 5でDIは業務系に要るのか←既公開
- Part 6:業務イントラの認証— Windows認証/ Forms認証/ Cookie←既公開
- Part 7: ASP.NET MVC 5でCSSが効かない時に確認する10項目←既公開・Step 5の症状切り分けの考え方はこちら
- Part 8: ASP.NET MVC 5をIISにデプロイする初手 ←この記事
- Part 9:トラブルシューティングチェックリスト←公開予定
- 📖連載まとめ・目次←公開予定
関連記事
- WinFormsのFormとRazor Viewの対応関係を業務SEが一日で腹落ちさせる—連載#1 / HTML/ASP.NETレイヤー分離思考の起点はこちら
- ASP.NET MVC 5で使えるORM 3択—連載#4 / 本番/Dev接続文字列の事故予防はこちら
- ASP.NET MVC 5でCSSが効かない時に確認する10項目—連載#7 / Web系お作法のハマり全般はこちら
以上!


コメント