IIS 10 の「Default Web Site」配下に複数アプリを置いて運用していると、「サイト全体は動かしたまま、特定の Web アプリだけ安全に止めたい」というニーズが頻出します。本記事は ASP.NET Web Forms(.NET Framework 4.8)を前提に、最短手順から本格運用まで“実務で使える”停止・再開のやり方、Web.config の安全な編集、そして「停止」メニューが見当たらない理由までを、一気通貫で整理します。
想定シナリオとゴール
前提は次のとおりです。
- OS:Windows Server(IIS 10)
- サイト:Default Web Site(
http://*:80/等にバインド) - 同サイト配下に複数のアプリ(例:
/Intranet,/Reports,/LegacyForms) - 対象:ASP.NET Web Forms(.NET Framework 4.8) の 1 アプリのみを止めたい
本記事のゴールは、対象アプリだけを意図通りに停止・再開し、Web.config を安全に編集でき、さらになぜ「停止」メニューが見えないのかまで説明できる状態になることです。
結論の要約(まずは全体像)
| 手段 | できること | 制約・注意 |
|---|---|---|
| ① アプリ専用のアプリケーション プール(App Pool)を停止 | そのプールに割り当てられたアプリを丸ごと停止。長時間メンテやリソース節約に最適。 | 同じプールを共有している他アプリも止まる。各アプリを別プールに分離しておくのが前提。 |
② App_Offline.htm を置く | 対象アプリのルートに置くだけで、そのアプリだけが 503 応答。メンテ画面を自由に表示可能。 | ワーカー プロセスは動作継続。アクセスは遮断されるが、CPU/メモリ節約は限定的。 |
| ③ 仮想ディレクトリを「アプリケーション」に昇格 | まだ「アプリケーション」でないなら、IIS マネージャで「アプリケーションに変換」。専用 App Pool を割り当てれば ① が使える。 | フォルダーのままでは個別停止不可。アプリ化+プール分離が必須。 |
| ④ Web.config を編集 | 保存でアプリが自動リサイクル(再起動)されるため、通常は停止不要で設定変更が可能。 | ロックなどで編集できない場合は、① プール停止か② App_Offlineで先に解放してから。 |
最短ルート:数分のメンテなら App_Offline.htm
ユーザーにはメンテナンス中の画面を出しつつ、アプリだけを一時的に止めたい場合、最速・最少手順は App_Offline.htm です。以下の 2 ステップで完了します。
- 対象アプリの物理ルート(例:
C:\inetpub\wwwroot\LegacyForms)に、App_Offline.htmを配置。 - メンテ終了時に、同ファイルを削除。
App_Offline.htm が存在する間、対象アプリへの全リクエストは 503 Service Unavailable となり、ファイルの内容がそのまま返ります。HTML を自由に書けるので、独自のメンテメッセージや再開予定時刻、連絡先などを表示できます。ファイル名は大文字小文字を問いません(App_offline.htm でも可)。
サンプル:最小のメンテ画面
<!doctype html>
<meta charset="utf-8">
<title>メンテナンス中です</title>
<style>body{font:16px/1.7 system-ui;margin:5vw} .box{max-width:640px}</style>
<div class="box">
<h1>システムメンテナンス中</h1>
<p>現在、<strong>LegacyForms</strong> アプリはメンテナンスのため停止中です。</p>
<p>ご不便をおかけしますが、しばらくしてから再度お試しください。</p>
</div>
PowerShell での配置・撤去の自動化
# 置く
$root = "C:\inetpub\wwwroot\LegacyForms"
$offline = Join-Path $root "App_Offline.htm"
Set-Content -Path $offline -Value @'
<!doctype html><meta charset="utf-8"><title>メンテナンス中</title>
<h1>LegacyForms はメンテナンス中です</h1>
'@ -Encoding UTF8
# 撤去
Remove-Item -Path $offline -Force -ErrorAction SilentlyContinue
しっかり止める:アプリ専用 App Pool の停止
長時間の作業やリソース節約が目的なら、アプリ専用のアプリケーション プールを停止するのが王道です。これなら対象アプリのワーカー プロセス(w3wp.exe)を落とせます。
前提:アプリ=専用 App Pool の割当
対象アプリが他アプリと App Pool を共有していると、停止時に巻き添えが出ます。まずは「プール分離」を済ませましょう。
- IIS マネージャを開く。
- 左ペインのサーバー直下で「アプリケーション プール」を選択し、新しいアプリケーション プール…をクリック。
- 例:
LegacyFormsPoolを作成(.NET CLR v4.0 / 管理パイプライン:統合)。 - 左ペインで Default Web Site → 対象アプリ(例:
LegacyForms)を右クリック → 基本設定 → アプリケーション プール をLegacyFormsPoolに変更。
停止・再開の操作
専用 App Pool が割り当たっていれば、以下で個別停止が可能です。
- IIS マネージャ → アプリケーション プール → 対象プールを選択 → 停止(再開は 開始)。
PowerShell / appcmd での操作
# PowerShell(WebAdministration モジュール)
Import-Module WebAdministration
# 停止
Stop-WebAppPool -Name "LegacyFormsPool"
# 状態確認
(Get-WebAppPoolState -Name "LegacyFormsPool").Value # Started / Stopped
# 再開
Start-WebAppPool -Name "LegacyFormsPool"
REM appcmd 版
%windir%\system32\inetsrv\appcmd list app
%windir%\system32\inetsrv\appcmd list apppool
%windir%\system32\inetsrv\appcmd stop apppool /apppool.name:"LegacyFormsPool"
%windir%\system32\inetsrv\appcmd start apppool /apppool.name:"LegacyFormsPool"
「まだフォルダー」なら:アプリケーションに昇格させる
左ペインでアイコンが「地球+フォルダー(仮想ディレクトリ)」になっている場合は、個別停止ができません。次の手順で「アプリケーション」に変換します。
- IIS マネージャで Default Web Site の下にある対象フォルダーを右クリック。
- アプリケーションに変換… を選択。
- エイリアス(通常はフォルダー名)と物理パスを確認。
- アプリケーション プール:新規作成をクリックし、専用プールを割り当て。
- 完了後、上記の App Pool 停止手順が利用可能になります。
Web.config を安全に編集するコツ
Web.config の保存はアプリのリサイクル(再起動)を誘発します。短時間の設定変更であれば、停止不要で編集して問題ありません。ただし、以下の点に注意してください。
| 観点 | ポイント |
|---|---|
| セッション | InProc セッションは再起動で消えます。影響を避けたい場合は一時的にメンテ画面へ誘導(App_Offline.htm)か、構成によっては StateServer 等の検討を。 |
| ファイル ロック | ウイルス対策やバックアップの影響でロックがかかることがあります。編集できないときはApp Pool 停止またはApp_Offline を先に適用。 |
| 機密情報 | 接続文字列等は aspnet_regiis.exe や DPAPI での暗号化を検討。 |
| 差分管理 | 変更多発の環境では Web.config 変換(Release/Debug など)や IaC(例:PowerShell Desired State)を使って再現性を担保。 |
典型的な編集例
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.8" />
<customErrors mode="On" defaultRedirect="~/Error.aspx" />
</system.web>
<appSettings>
<add key="FeatureX.Enabled" value="false" />
</appSettings>
</configuration>
「停止」メニューが見えない理由
IIS 8 以降、サイト配下の「アプリケーション」を右クリックしても、「Manage Application > Stop(停止)」は表示されません。歴史的には IIS 7/7.5 に存在しましたが、裏ではアプリケーション プールの停止に過ぎず混乱を招いたため、UI としては廃止されました。以後、停止は App Pool 単位が原則です。一方、サイトの停止(Manage Website > Stop)は、バインド(ポート/ホスト名)のスコープであり、サイト全体が停止します。ここを取り違えないことが重要です。
GUI 操作の詳細手順(IIS マネージャ)
アプリの状態確認
- Default Web Site を選択 → 中央ペインで対象アプリをダブルクリック。
- 右の「操作」から 基本設定 を開き、アプリケーション プール名と物理パスを確認。
専用 App Pool の作成と割り当て
- 左ペイン「アプリケーション プール」→ 右ペイン「新しいアプリケーション プール…」。
- 名前(例:
LegacyFormsPool)、.NET CLR(v4.0)、パイプライン(統合)で作成。 - 対象アプリ → 右クリック「基本設定」→ プールを
LegacyFormsPoolに変更。 - 必要に応じて「詳細設定」からリサイクル、アイドル タイムアウト、同時要求数などを調整。
停止・再開
- 「アプリケーション プール」で
LegacyFormsPoolを選択。 - 右ペイン「停止」。再開は「開始」。
CLI による一括運用(サンプル スクリプト)
param(
[Parameter(Mandatory=$true)] [ValidateSet("Start","Stop","Offline","Online")] $Action,
[Parameter(Mandatory=$true)] [string] $AppName, # 例: "Default Web Site/LegacyForms"
[Parameter(Mandatory=$true)] [string] $PoolName, # 例: "LegacyFormsPool"
[Parameter(Mandatory=$true)] [string] $AppRoot # 例: "C:\inetpub\wwwroot\LegacyForms"
)
Import-Module WebAdministration
$offline = Join-Path $AppRoot "App_Offline.htm"
switch ($Action) {
"Stop" { Stop-WebAppPool -Name $PoolName }
"Start" { Start-WebAppPool -Name $PoolName }
"Offline" { Set-Content -Path $offline -Value "メンテ中" -Encoding UTF8 }
"Online" { if (Test-Path $offline) { Remove-Item $offline -Force } }
}
# 簡易疎通
try {
$url = "[http://localhost/](http://localhost/)" + ($AppName -split "/")[1]
(Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 5).StatusCode | Out-Null
Write-Host "OK $url"
} catch {
Write-Host "NG (停止/オフライン中か、URL が異なる可能性)"
}
よくある落とし穴と回避策
- 同一プールの巻き添え停止:プール共有は短期的な省力化に見えて、運用で必ず詰まります。アプリ=プール 1:1 を原則に。
- セッション消失:Web.config 保存やプール停止で
InProcセッションは失われます。影響を最小化するため、予約時間にApp_Offline.htmを出して事前告知+操作ロック推奨。 - オーバーラップ リサイクルと競合:既定の定期リサイクルが重なると復帰に時間がかかることがあります。メンテ前に手動でリサイクル、またはスケジュール制御を。
- 「起動前読み込み」(startMode/Preload):サイトやアプリのプリロード設定が有効だと、App Pool 開始直後に初回リクエストが重くなりにくい反面、
App_Offline.htmと開始・停止のタイミングが複雑化します。手順を固定化しましょう。 - 権限不足でファイル操作不可:デプロイ ユーザーに対し、対象アプリの物理フォルダーへ書き込み権限(
Modify)を付与。 - URL ルーティングとの干渉:
App_Offline.htmは 物理ファイルとして扱われるため、ルーティング設定に関わらず優先されます。
運用ベストプラクティス(実環境で効く習慣)
- 命名規約:
<Site>_<App>_Pool形式(例:Default_LegacyForms_Pool)で一目で対応関係がわかるように。 - ヘルスチェック URL:
/healthzのような軽量ページを用意。メンテ時はApp_Offline.htmの文言にステータスを併記。 - 変更の原子化:Web.config の編集とバイナリ入替を同時に行わない。先に
App_Offline.htm、次にファイル操作、最後に撤去という型を守る。 - バックアップ:
%windir%\system32\inetsrv\config配下(applicationHost.config等)の自動バックアップを有効化。 - 手順書の 2 系統化:GUI 版(誰でも実行可)と CLI 版(自動化・CI/CD 用)の両方を用意。
ケーススタディ:3 アプリ同居の「Default Web Site」
Intranet、Reports、LegacyForms の 3 つが同居する例で考えます。
| アプリ | パス | 物理ルート | App Pool | 備考 |
|---|---|---|---|---|
| Intranet | /Intranet | C:\inetpub\wwwroot\Intranet | IntranetPool | 社内ポータル(稼働優先) |
| Reports | /Reports | C:\inetpub\wwwroot\Reports | ReportsPool | バッチ連携あり |
| LegacyForms | /LegacyForms | C:\inetpub\wwwroot\LegacyForms | LegacyFormsPool | 今回の停止対象 |
この構成なら、LegacyForms を止めても他アプリには波及しません。メンテ中は App_Offline.htm を表示して問い合わせ先を明記し、作業後にファイル削除+プール開始で即復帰できます。
停止手順のテンプレート(貼って使える)
- アプリケーションであることを確認(仮想ディレクトリならアプリに変換)。
- 専用 App Pool を割当(共有はやめる)。
- メンテ開始:
・短時間 →App_Offline.htmを投入(ユーザーに明示)。
・長時間/リソース節約 → App Pool を停止。 - Web.config やコードを更新(必要に応じて構文チェック)。
- メンテ終了:
・App Pool を開始。
・App_Offline.htmを削除。
・健康チェック URL で疎通確認/イベントログ確認。
トラブルシュート早見表
| 症状 | 原因の当たり | 確認ポイント | 対処 |
|---|---|---|---|
| 「停止」ボタンが見当たらない | アプリ停止 UI は廃止 | App Pool 名がわかるか | プールを停止/開始で対応 |
| プール停止で他アプリも止まった | プール共有 | 各アプリの割当プール | 専用プールを新設し再割当 |
| Web.config が保存できない | ファイル ロック | プロセス/AV 稼働 | 先にプール停止 or App_Offline.htm |
| メンテ画面が出ない | 配置パス違い・拡張子違い | 物理ルート直下か、名前は App_Offline.htm か | 正しい場所・名前に置き直す |
| 復帰が遅い | 初回 JIT/キャッシュ | プリロード設定/ウォームアップ | 再開後にウォームアップ用 GET を実行 |
補足:HTTP ステータスと周辺動作
App_Offline.htm:既定で 503。ロードバランサや監視の「ダウン判定」を適切に設定してください。- App Pool 停止:対象アプリへの接続は直ちにできなくなります。サイト全体ではなく、そのプールに属するアプリのみが影響を受けます。
- Web.config 保存:アプリ ドメインの再起動で一時的に 500 系が観測される場合があります。監視は短期スパイクを許容する設定に。
運用チェックリスト(配布用)
- 対象はアプリケーションである(仮想ディレクトリではない)。
- アプリ=App Pool は 1:1。プール名は命名規約に従う。
- メンテ開始前に関係者へ告知(影響範囲/予定時刻)。
App_Offline.htmはテンプレ化・配布し、毎回の文言ブレをなくす。- 作業前後でイベント ログ/IIS ログ/APM の 3 点セットを確認。
- 復帰後のウォームアップ実行(主要ページへの GET)。
- 万一のロールバック手順(バックアップ、差分)を台本化。
FAQ(現場でよく聞かれる質問)
Q. App_Offline.htm と App Pool 停止、どちらを選ぶべき? A. ユーザー告知や短時間保守は App_Offline.htm。長時間・リソース節約・確実な停止は App Pool 停止。併用も有効です(先にオフライン表示→プール停止)。 Q. Default Web Site 自体を停止したら早い? A. サイト停止はバインド全体が止まるため、他アプリも含め全滅します。部分停止の目的に反します。 Q. ASP.NET Core でも同じ? A. 本記事は Web Forms(.NET Framework 4.8)を前提にしています。ASP.NET Core でも app_offline.htm は有効ですが、ホスティング方式(IIS 直下 or 反転プロキシ)で挙動が違う点に注意。 Q. 停止中に 503 を 200 にしたい A. 監視や LB の設計次第ですが、基本は 503 が正しいシグナルです。どうしても 200 が必要なら、別 URL(例:/maintenance)を 200 で返し、LB にはそのパスをヘルスチェックに指定する設計を。
まとめ:これが「安全に 1 アプリだけ止める」基本形
アプリ=App Pool を 1:1 に分離し、短時間なら App_Offline.htm、長時間なら App Pool 停止。Web.config の編集は通常停止不要だが、ロック時は先にオフライン化またはプール停止。サイト停止とアプリ停止は別物であることを理解し、IIS マネージャ/PowerShell の両輪で運用を標準化すれば、Default Web Site を落とさずに目的のアプリだけを確実にコントロールできます。
付録:貼って使えるコマンド集
REM --- appcmd での確認・割当・停止/開始 ---
%windir%\system32\inetsrv\appcmd list app
%windir%\system32\inetsrv\appcmd list apppool
%windir%\system32\inetsrv\appcmd set app /app.name:"Default Web Site/LegacyForms" /applicationPool:"LegacyFormsPool"
%windir%\system32\inetsrv\appcmd stop apppool /apppool.name:"LegacyFormsPool"
%windir%\system32\inetsrv\appcmd start apppool /apppool.name:"LegacyFormsPool"
# --- PowerShell WebAdministration ---
Import-Module WebAdministration
# プール作成
New-WebAppPool -Name "LegacyFormsPool"
# アプリに割当
Set-ItemProperty "IIS:\Sites\Default Web Site\LegacyForms" -Name applicationPool -Value "LegacyFormsPool"
# 停止 / 開始 / 状態
Stop-WebAppPool -Name "LegacyFormsPool"
Start-WebAppPool -Name "LegacyFormsPool"
Get-WebAppPoolState -Name "LegacyFormsPool"
# App_Offline の配置と撤去
$root = "C:\inetpub\wwwroot\LegacyForms"
Set-Content (Join-Path $root "App_Offline.htm") "<h1>メンテ中</h1>" -Encoding UTF8
Remove-Item (Join-Path $root "App_Offline.htm") -Force

コメント