Visual StudioでASP.NET Core 6をSelf-contained(win-x64)発行し、Windows Server 2012 R2(IIS 8.5)へ配置したら、HTTP 500.21「Handler ‘aspNetCore’ has a bad module ‘AspNetCoreModuleV2’」で起動しない――そんなときの原因は、ランタイムではなくIIS連携用モジュール(ANCM)の不足であるケースがほとんどです。確認手順と最短の解決策を整理します。
HTTP 500.21「AspNetCoreModuleV2 が不正」とは
IISでHTTP 500.21が出るときは、アプリのコードが実行される前段階、つまりIISの構成(モジュール/ハンドラー)の読み込みで止まっています。今回の典型例が次のメッセージです。
- Handler “aspNetCore” has a bad module “AspNetCoreModuleV2” in its module list
これは「web.config に modules="AspNetCoreModuleV2" と書いてあるが、サーバー側に AspNetCoreModuleV2(ANCM: ASP.NET Core Module) が存在しない/正しく登録されていない」という意味です。.NET 6 ランタイムの有無とは別軸で発生します。
| 見えている症状 | 原因の当たり | まず見る場所 |
|---|---|---|
| HTTP 500.21(AspNetCoreModuleV2 が不正) | ANCM(AspNetCoreModuleV2)が未導入/破損 | IIS マネージャー → サーバー →「モジュール」 |
| HTTP 500.30(プロセス起動失敗) | アプリ起動時例外、依存DLL不足、環境変数など | イベント ビューアー / stdoutログ |
| dotnet で dll は起動するが IIS では起動しない | IIS連携の前提(ANCM、権限、アプリプール設定)不足 | web.config / アプリプール / フォルダ権限 |
結論:Self-contained 配布でも IIS 連携には ANCM が必須
「Self-contained(自己完結)」は、アプリが動くための.NET ランタイム一式を発行物に同梱する方式です。これによりサーバーに .NET 6 ランタイムが入っていなくても、コンソール起動(exe直起動、または dotnet で dll 実行)なら動きます。
しかし、IISで aspNetCore ハンドラーを使ってホストする場合、IISは内部でネイティブモジュール(ANCM)を使って、要求の受け口・プロセス起動・標準入出力連携・リサイクル連動などを行います。Self-containedに含まれているのは主に「アプリ実行環境」であり、IIS連携のためのANCMは同梱されません。
ポイント:Self-contained =「.NET ランタイム不要」ではあるが、IIS で動かすなら ANCM(AspNetCoreModuleV2)だけはサーバー側に必要。
| 項目 | Self-contained(win-x64) | .NET Hosting Bundle(IIS Hosting) |
|---|---|---|
| アプリ実行に必要な .NET ランタイム | 発行物に同梱される | サーバーに共有ランタイムとして入る(同梱ではない) |
| IIS 連携モジュール(AspNetCoreModuleV2 / ANCM) | 含まれない | 入る(これが目的) |
| 目的 | サーバーにランタイムを入れられない/入れたくない場合の配布 | IISでASP.NET Coreアプリをホストできるようにする |
| 今回のエラー(500.21)への効果 | 単体では解決しない | 解決する |
サーバー側でまず確認すること(切り分け)
導入に入る前に、ANCMが未導入なのか、登録が壊れているのかを確認すると再発防止にもつながります。IIS 8.5(Windows Server 2012 R2)での代表的な確認点は次の通りです。
IIS マネージャーで「モジュール」に AspNetCoreModuleV2 があるか
- IIS マネージャーを開く
- 左のツリーでサーバー名をクリック
- 中央の一覧から「モジュール」を開く
- 一覧にAspNetCoreModuleV2が存在するか確認
存在しなければ、今回のエラーの原因はほぼ確定です(= Hosting Bundle で解決)。
コマンドで確認する(GUIが触れない環境向け)
管理者権限のコマンドプロンプト/PowerShellで、IISのモジュール一覧を確認します。
cd %windir%\System32\inetsrv
appcmd list modules | findstr /i AspNetCore
何も出てこなければ未導入の可能性が高いです。導入済みなら AspNetCoreModuleV2(環境によっては AspNetCoreModule も) が表示されます。
web.config で指定しているモジュール名が一致しているか
発行物の web.config に、次のような記述があるはずです。
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
ここで AspNetCoreModuleV2 を指定している以上、サーバーにそのモジュールが必要です。逆に、古いアプリで AspNetCoreModule を使っている場合は、そちらが必要になります(ただし現在はV2が一般的です)。
解決手順:.NET 6 Hosting Bundle をインストールする
対処はシンプルで、サーバーに.NET 6 Hosting Bundle(IIS Hosting を含む)を追加します。これで AspNetCoreModuleV2(ANCM)が導入され、IIS から ASP.NET Core アプリを起動できるようになります。
インストールの流れ
- サーバーに管理者権限でログイン
- Microsoft公式の「.NET 6 Hosting Bundle」を入手し実行
- インストール完了後、必要に応じてIISを再起動(
iisreset) - 再度アクセスして 500.21 が解消したか確認
再起動は「サーバー再起動」までは不要なことが多いですが、IISが古い構成を掴んだままの環境もあるため、まずは iisreset を推奨します。
iisreset
インストール後の確認
- IIS マネージャー → サーバー →「モジュール」に AspNetCoreModuleV2 が出現する
- アプリにアクセスして、500.21 ではなくアプリ本来のレスポンスが返る
- 必要に応じてイベント ビューアー(Windowsログ → アプリケーション)でエラーが消えていることを確認
アプリプール設定でハマりやすいポイント
ANCMを入れて 500.21 が解消しても、次は「起動はするが 500.30 になる」「静的ファイルが出ない」といった別の壁に当たりがちです。IISの基本設定も合わせて押さえておくと、切り分けが速くなります。
| 設定項目 | 推奨 | 理由/補足 |
|---|---|---|
| アプリプールの .NET CLR バージョン | マネージド コードなし | ASP.NET Core は IIS の .NET CLR で動かすのではなく、ANCM経由でホストするため |
| パイプライン モード | 統合 | 一般的な推奨。特別な事情がなければ統合でOK |
| 32ビット アプリケーションの有効化 | win-x64 なら False | Self-contained(win-x64) は 64bit 実行が前提 |
| アプリケーションプールのID(権限) | 最低限の権限 + 必要に応じて変更 | 配置フォルダの読み取り/実行、ログ出力先の書き込み権限に注意 |
web.config の見どころ(Self-contained / inprocess)
質問にある通り、発行した web.config に hostingModel="inprocess" が入っているのは正常です。むしろinprocess は高速で、特別な事情がなければこのままで問題ありません。
Self-contained の典型例(processPath は exe)
Self-contained では、起動対象は基本的に「生成された exe」になります(Visual Studio の発行が自動で合わせてくれます)。イメージとしては次の形です。
<aspNetCore processPath=".\FeriasForm.Server.exe"
arguments=""
hostingModel="inprocess"
stdoutLogEnabled="false" />
framework-dependent の典型例(processPath は dotnet)
一方で、framework-dependent(サーバーにランタイムが必要)だと次の形が一般的です。
<aspNetCore processPath="dotnet"
arguments=".\FeriasForm.Server.dll"
hostingModel="inprocess" />
どちらの形でも、IISがaspNetCoreハンドラーを処理するには ANCM(AspNetCoreModuleV2)が必要です。今回の 500.21 は、この前提が満たされていないことで起きています。
トラブルシュート用途の stdout ログ
起動に失敗する場合は一時的に stdout ログを有効化すると原因が追いやすくなります。ただしログにはパスや例外情報が出るため、運用に入ったら無効化してください。
<aspNetCore processPath=".\FeriasForm.Server.exe"
hostingModel="inprocess"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout" />
このとき logs フォルダを作成し、アプリプールIDに書き込み権限を付与するのがコツです(権限がないとログ自体が出ません)。
「exe と同名の dll がある」のは正常
Self-contained で発行すると、出力先にアプリ名.exe とアプリ名.dll が並ぶことがあります。これは異常ではなく、ざっくり言うと次の役割分担です。
- xxx.exe:起動用ホスト(エントリポイント)。必要なランタイムや依存を読み込み、アプリを立ち上げる
- xxx.dll:アプリ本体(IL)。実際のASP.NET Coreアプリケーションコードが入っている
ローカルで動作確認するときは、基本は次のどちらかです。
- Self-contained:exe を直接実行(
.\FeriasForm.Server.exe) - framework-dependent:dotnet で dll を実行(
dotnet .\FeriasForm.Server.dll)
逆に、dotnet FeriasForm.Server.exe のように「dotnet に exe を渡す」と、想定外のエラーになることがあります。exe は dotnet のホストではなく、それ自体がホストだからです。
.NET Core 2.x と .NET 6 は共存できる?
結論としては基本的に共存可能です。.NET(.NET Core含む)はサイドバイサイドで複数バージョンをインストールでき、既存アプリを壊さずに新しいランタイム/ホスティングを追加する運用が一般的です。
ただし、ここで混同しやすいのが「ランタイム」と「IISホスティング(ANCM)」です。
- ランタイム:アプリが動く実行環境。Self-containedなら原則不要
- ANCM:IISがASP.NET Coreを起動・プロキシするためのIIS拡張。IISホストなら必要
今回のケースは「サーバーの dotnet --info に .NET Core 2.1 しか出ない」こと自体が問題というより、ANCMが入っていない(または古い)ことが問題でした。Hosting Bundle を入れたことで解決し、既存の環境とも共存できた、という流れになります。
| 質問でよく出る疑問 | 答え | 補足 |
|---|---|---|
| Self-contained なのにサーバーに何か入れる必要がある? | ある(ANCM) | IIS連携は別コンポーネント。アプリの同梱物では代替できない |
| サーバーに .NET 6 ランタイムが無いのが原因? | 原因はランタイムではなく ANCMの不足 | ただし Hosting Bundle を入れるとランタイムも一緒に入る |
| .NET Core 2.x と .NET 6 は共存できる? | できる | サイドバイサイドが前提。既存アプリの影響は最小 |
それでも動かない場合の追加チェック
500.21 が解消しても、次の原因で止まることがあります。ここまで押さえると、現場での復旧がかなり早くなります。
配置フォルダの権限(特にログ出力や一時ファイル)
- アプリ配置フォルダに、アプリプールIDが読み取り/実行できるか
- stdoutログやアップロード先など、書き込みが必要な場所に書き込み権限があるか
必要な IIS 機能が入っているか
静的ファイル(React/Viteのビルド成果物)をIISから配信する場合、IISの役割/機能として「静的コンテンツ」が無効だと 404 になりやすいです。また、APIのルーティングに影響するため、基本機能(既定のドキュメントなど)も確認しておきましょう。
アプリが listen しているポート/URL が期待通りか
コンソールで http://localhost:5000 で動くのにIISだと動かない場合、IISでは ANCM が inprocess で w3wp に組み込むため、Kestrel が固定ポートを listen する形とは挙動が変わります。特に out-of-process に切り替えた場合は、ASPNETCORE_URLS や --urls の指定がぶつかっていないかを確認してください。
本番環境の例外が隠れている(500.30 対策)
本番では詳細エラーが隠れていることが多いので、切り分け時は次の順で情報を集めると効率的です。
- イベント ビューアー(アプリケーションログ)
- 一時的に stdout ログを有効化
- 発行フォルダで exe 直起動し、例外やポート競合を確認
まとめ
HTTP 500.21「AspNetCoreModuleV2 が不正」は、アプリの問題ではなく IIS 側の前提不足で起きる代表例です。Self-contained 配布であっても、IISでホストする以上はASP.NET Core Module(ANCM / AspNetCoreModuleV2)が必要になります。
- 500.21 は「AspNetCoreModuleV2 がサーバーにない/登録されていない」サイン
- 解決策は.NET 6 Hosting Bundle(IIS Hosting を含む)の導入
- 導入後は
iisreset、アプリプールは「マネージド コードなし」など基本設定も確認 - .NET Core 2.x と .NET 6 の共存は基本的に可能(サイドバイサイド)
まずは Hosting Bundle を入れて 500.21 を消し、その上で 500.30 や権限・静的ファイル配信など、次の段階のトラブルシュートへ進めるのが最短ルートです。

コメント