Azure Application Gateway(WAFv2)配下にAzure Storageを置き、パブリックアクセス無効・Private Endpointのみ・匿名/SASなしという堅牢構成にすると、正常性プローブが403/409になりバックエンドがUnhealthyになりがちです。本記事では2xxを返す現実的な設計と設定手順、落とし穴を整理します。
前提となる構成と「なぜプローブが落ちるのか」
今回の前提は次のとおりです。
- バックエンドは Azure Storage アカウント(Blob)
- クライアント→Storage への入口として Azure Application Gateway(WAFv2) を利用
- Storage 側は パブリック ネットワーク アクセス無効、プライベート エンドポイントのみ
- 匿名アクセス無効(「Allow Blob public access」を無効にしている/コンテナーを非公開にしている等)
- SAS トークンも使わない(もしくは組織ポリシーで禁止)
この状態で Application Gateway のバックエンド正常性プローブ(ヘルス プローブ)を HTTP/HTTPS で投げると、プローブが以下の理由で失敗しやすくなります。
| 起きる現象 | 典型的な原因 | よく見るステータス |
|---|---|---|
| 到達できずタイムアウト | Private Endpoint/DNS/ルート/NSG などネットワーク要因で Storage のプライベート IP に到達できていない | 502/タイムアウト(AppGW 側) |
| 到達はするが Unhealthy のまま | Storage が認証を要求するのに、プローブが認証ヘッダーやクエリを付与しない(または Host ヘッダー不整合) | 403/409/400 |
| Host ヘッダーが合わずに失敗 | バックエンドに IP を指定していて SNI/Host ヘッダーが Storage の証明書名と一致しない | 400/SSL 失敗 |
ポイントは、Application Gateway のプローブは基本的に 「指定した URL に対して GET を投げ、返ってきたステータスで正常性を判定する」という仕組みであることです。Storage 側が「匿名不可・SAS不可・Public 不可」のままだと、“正しい認証” を伴わないプローブは 2xx になりにくいのが本質です。
さらに、Storage を HTTPS で叩く場合は 証明書の名前(ホスト名)にも厳格です。バックエンドに Private Endpoint の IP を直接入れていると、SNI/Host が噛み合わず失敗することがあるため、ホスト名上書きは必須項目だと考えてください。
まず切り分けるべきは「ネットワークの到達性」
いきなりプローブ設定に入る前に、Application Gateway から Storage へ プライベート エンドポイント経由で到達できているかを先に潰すと、無駄な試行錯誤が減ります。
確認観点チェックリスト
- Application Gateway が存在する VNet から、Storage の FQDN が プライベート IP に名前解決できている
- 強制トンネリングや UDR により、Storage 宛トラフィックが意図せず別経路へ流れていない
- カスタム DNS を使っている場合、
privatelink.blob.core.windows.net(必要に応じてprivatelink.web.core.windows.netなど)を解決できる - Application Gateway のサブネット側で、宛先 443 をブロックする NSG ルールが入っていない
同一 VNet 内のテスト VM での検証例
Application Gateway と同じネットワーク(同一 VNet か、適切にピアリングされた VNet)に、検証用 VM を一時的に置けるなら、次の確認が一番早いです。
nslookup <ストレージ名>.blob.core.windows.netを実行し、プライベート IP が返ることを確認- Web エンドポイントを使う予定なら
nslookup <静的サイトのホスト名>でも同様に private 側を引くことを確認 - 既知のパスに対して
curl -I https://<ホスト名>/<パス>を実行し、TLS 接続と HTTP 応答を確認
この段階で名前解決・到達性が崩れていると、プローブに何を設定しても Unhealthy になります。逆に、ここが固まっていれば、次は「2xx を返すエンドポイント設計」に集中できます。
2xx を返すための代表的な解決パターン
今回の制約(匿名不可、SAS不可、Public 不可)をそのまま守ろうとすると、Application Gateway のプローブだけで 2xx を返すのは難易度が上がります。実務では、次のいずれかに落ち着くことが多いです。
| パターン | 2xx を返す仕組み | メリット | 注意点 | 向いているケース |
|---|---|---|---|---|
| 静的 Web サイト($web)にヘルスページ | $web の固定ファイルが常に 200 を返す | 構成がシンプル/プローブが安定 | 匿名読み取りを「どこまで許容するか」の整理が必要(ただし Private Endpoint 前提なら閉域に寄せられる) | 最短で安定させたい/運用負荷を下げたい |
| ヘルスチェック専用コンテナーのみ匿名 | 特定コンテナー内の固定ファイルが 200 | 匿名範囲を最小化できる | アカウントの「Allow Blob public access」や組織ポリシー次第で不可 | 静的 Web サイトを使いにくい/匿名範囲をさらに狭めたい |
| SAS をプローブに付与 | SAS 付き URL で 200 | 匿名を開けずに 200 を返せる | SAS の漏えい・期限管理・ローテーションが課題/「SAS 禁止」要件と衝突しやすい | SAS を許容でき、運用で管理できる |
| ヘルスプロキシ(中継)を用意 | AppGW→中継は 200、中継→Storage は認証付きで疎通確認 | Storage を完全に閉域&匿名/SAS なしを維持できる | 追加コンポーネントが必要(Function/Container 等) | セキュリティ要件が厳格で、匿名/SAS を絶対に避けたい |
静的 Web サイト機能でヘルス プローブを成功させる
「プローブが 200 を返す URL を用意する」という意味で、静的 Web サイト($web)を使うのは非常に現実的です。質問事例でも、この方法で Unhealthy を解消できたケースが多いです。
設計の考え方
- プローブ先は 必ず存在する固定ファイルにする(例:
healthz.html) - ファイルの内容はシンプルでよい(例:
OKの1行)。必要ならデプロイ日時などを埋め込む - 「匿名で読める」こと自体が気になる場合でも、Public network access 無効 + Private Endpoint を組み合わせれば、インターネットからは到達できない構成に寄せられる
なお、ストレージ アカウントで 「Allow Blob public access」 を無効にしている場合、静的 Web サイト機能の有効化や動作が制限されることがあります。組織ポリシーで厳格に禁止されている環境では、後述の「ヘルスプロキシ」方式の方が説明しやすいこともあります。
静的 Web サイトのエンドポイントは二種類ある
静的 Web サイトを使う場合、ヘルスチェックで叩く先は大きく2パターンあります。
| プローブ先 | 例 | 特徴 | Private Endpoint の要件 |
|---|---|---|---|
| Web エンドポイント(静的サイト専用) | https://<ストレージ名>.<任意>.web.core.windows.net/healthz.html | 静的 Web サイト向け。基本的に匿名読み取りで 200 を返しやすい | 「web」サブリソース用の Private Endpoint と DNS(privatelink.web.core.windows.net)が必要になることが多い |
| Blob エンドポイントで $web を参照 | https://<ストレージ名>.blob.core.windows.net/$web/healthz.html | Blob と同じ経路。環境によっては匿名で 200 を返すが、コンテナー公開設定やポリシーの影響を受けやすい | blob 用の Private Endpoint と DNS(privatelink.blob.core.windows.net)で到達できる |
迷ったら、まずは ポータルの「静的ウェブサイト」ブレードに表示される「プライマリ エンドポイント」(= Web エンドポイント)を使うのが安全です。組織ポリシーや Private Endpoint のサブリソース選択により使えない場合は、Blob エンドポイント側の $web を使う落としどころを検討します。
設定手順(Storage 側)
- Storage アカウントで Blob サービス → 静的ウェブサイト を有効化する
- インデックス ドキュメント名に
index.htmlを指定(ヘルス専用ならhealthz.htmlを置いてもOK) $webコンテナーに、ヘルスチェック用のファイルをアップロードする(例:healthz.html)
ヘルスチェック用ファイルの例です。
OK
Private Endpoint と DNS の注意点(Web エンドポイントを使う場合)
Public network access を無効にしている構成では、Web エンドポイントもインターネットからは到達できません。VNet 内からアクセスさせるには、次のいずれかが必要になります(環境差があります)。
- Storage の Private Endpoint を 「web」サブリソースで追加作成する
- Private DNS ゾーン
privatelink.web.core.windows.netを作成し、Application Gateway のある VNet にリンクする
このあたりは「同じ VNet の検証 VM で名前解決してみる」と最短で判断できます。Web エンドポイントが public 側に解決されるなら、Private Endpoint/DNS が不足しています。
設定手順(Application Gateway 側:プローブ)
プローブ設定で重要なのは Host ヘッダーと パスです。Storage は要求先のホスト名に敏感なので、ここがズレると 2xx になりません。
| 項目 | 推奨値(例) | 意図 |
|---|---|---|
| プロトコル | HTTPS | Storage は基本 HTTPS 前提。証明書検証と SNI を意識する |
| ホスト | <静的サイトのホスト名> または <ストレージ名>.blob.core.windows.net | Storage の証明書名と一致させる(SNI/Host ヘッダーの整合) |
| パス | /healthz.html または /$web/healthz.html | 必ず存在する固定ファイルを指定し 200 を取りに行く |
| 許容ステータス | 200-399(要件が 2xx 固定なら 200 のみに絞る) | リダイレクトが入る構成でも落ちにくくする |
| 間隔 / タイムアウト | 30秒 / 30秒(例) | Backend の特性に合わせて調整。短すぎると瞬断で Unhealthy になりやすい |
バックエンド プールと HTTP settings の設定例(ここがズレると 2xx にならない)
プローブだけを直しても、バックエンド プールや HTTP settings の Host/SNI がズレていると失敗します。最小構成の例をまとめます。
| 設定箇所 | 推奨(例) | 理由 |
|---|---|---|
| バックエンド プール | ターゲット:FQDN 値: <ストレージ名>.blob.core.windows.net または <静的サイトのホスト名> | DNS で Private Endpoint の IP を引かせつつ、TLS のホスト名整合(SNI)も取りやすい |
| HTTP settings | プロトコル:HTTPS / ポート:443 ホスト名:バックエンドから選択(または上書きで Storage のホスト名) SNI:有効 | Storage は証明書名チェックが厳しいため、IP 宛接続でもホスト名を正しく送る |
| 証明書 | 通常は “信頼されたルート CA” で検証(追加の証明書設定は不要なことが多い) | Storage の証明書は一般的な公開 CA で発行されているため |
バックエンド プールに Private Endpoint の IP を直接入れる方法もありますが、その場合は HTTP settings でホスト名上書きと SNI がより重要になります。まずは FQDN 登録 + Private DNS で成立させる方が運用は安定します。
さらに確実にするための設定のコツです。
- バックエンド HTTP 設定(HTTP settings)で 「バックエンド アドレスからホスト名を選択」または 「特定のホスト名で上書き」を有効にし、Storage のホスト名を送る
- バックエンドに IP(Private Endpoint の IP)を入れている場合は、HTTP settings 側で SNI とホスト名の上書きが必須になることが多い
- 「/」のようなルートに対する応答は環境差が出やすいので、固定ファイルを置いてそこを叩く方が安定する
セキュリティ上の落としどころ
静的 Web サイトは「匿名で 200 が返る」ことが利点ですが、同時に気になる点でもあります。実務上は次の運用でリスクを最小化できます。
- 公開するのは ヘルスチェック専用の1ファイルのみ(個人情報・業務データを置かない)
- Public network access は無効のままにし、VNet 内(Private Endpoint)からしか到達できない構成にする
- ファイル内容に機微情報を入れない(「OK」だけ、もしくは無害な固定文字列)
マネージド ID を使った「認証付きプローブ」を検討する場合
「匿名も SAS も使わず、Azure AD(RBAC)で認可した状態で 2xx を返したい」という発想で、Application Gateway 側にマネージド ID を割り当て、Storage 側でロール付与してプローブする案が検討されることがあります。
ロール付与でよくある誤解
Storage アカウントに対して単に Reader ロールを付与しても、Blob のデータ面(コンテナー/Blob の読み取り)には権限が足りません。データアクセスが必要なら、用途に応じて次のような データ プレーンのロールが必要です。
- 読み取りだけ:Storage Blob Data Reader
- 書き込みも含む:Storage Blob Data Contributor など
それでも「AppGW のプローブだけ」で完結しにくい理由
ここが重要です。Storage を Azure AD で認証してアクセスするには、通常 OAuth の Bearer トークンをリクエストに付ける必要があります。Application Gateway のヘルス プローブは、固定のヘッダーを付けることはできても、マネージド ID からトークンを取得して自動更新しながら付与するといった “動的認証” を前提に作られていません。
そのため、マネージド ID を有効化してロールを付けたとしても、プローブが 2xx を返せるとは限りません。採用を検討する場合は、SKU/機能の制約や実際の挙動をテスト環境で必ず検証し、難しいようなら後述の「ヘルスプロキシ」方式に切り替えるのが堅実です。
ヘルスチェック専用コンテナーだけを匿名読み取りにする
静的 Web サイト機能を使いたくない、または web サブリソースの Private Endpoint を増やしたくない場合は、ヘルスチェック専用コンテナーだけ匿名読み取りを許可する妥協案が現実的です。
構成イメージ
- コンテナー名:
health - ファイル:
healthz.txt(内容は “OK” など) - プローブ先:
https://<ストレージ名>.blob.core.windows.net/health/healthz.txt
設定手順(Storage 側)
- ストレージ アカウント設定で 「Allow Blob public access」が組織ポリシー上許可されているか確認する(無効だとコンテナー公開ができません)
healthコンテナーを作成し、パブリック アクセス レベルをBlob(推奨)に設定するhealthz.txtをアップロードする
ここでの「匿名」は ネットワーク的に閉じた範囲でのみ成立させるのが肝です。Public network access を無効にし、Private Endpoint のみで到達させるなら、インターネットから “誰でも読める” 状態にはなりにくくなります。
Application Gateway 側のプローブ設定
Host は <ストレージ名>.blob.core.windows.net、Path は /health/healthz.txt とし、200 を許容する形にします。Host ヘッダー上書きの考え方は静的 Web サイトと同様です。
SAS をカスタム プローブに付与する(要件が許す場合)
「匿名アクセスは避けたいが、2xx を返す URL を用意したい」という場合、SAS を使ったヘルスチェックは合理的です。ただし、今回の前提では “SAS 無効/禁止” になっているため、採用するには要件整理が必要です。
設定の考え方
- 権限は最小化(例:特定の1ファイルへの Read のみ)
- 期限は短くし、ローテーションを前提にする(「長期限 SAS を固定で貼る」は避ける)
- プローブ設定に秘密情報(SAS)を入れるなら、設定ファイルや IaC の扱いを厳格にする
プローブの指定例
多くのケースでは、SAS は Authorization ヘッダーよりも クエリ文字列として URL に付与する方が扱いやすいです。
Path: /health/healthz.txt?sv=...&sr=b&sig=...
別案として、Authorization: SharedAccessSignature ... のようにヘッダーで渡す構成が紹介されることもありますが、いずれにせよ “固定値の秘密” を AppGW 設定に持ち込む点は同じです。漏えいリスクとローテーションを設計に含めてください。
完全に閉域を維持したいなら「ヘルスプロキシ」を置く
「匿名も SAS も絶対に使えない」「それでもプローブで 200 を返したい」という要求は、Application Gateway 単体ではかなり厳しくなります。現実解としては、プローブ用の中継(ヘルスプロキシ)を用意して、そこから Storage を認証付きで確認させます。
アーキテクチャ例
Application Gateway (probe: /health) ──> Health Proxy (200/500 を返す)
└─(Managed Identity / RBAC)─> Storage (Blob)
ヘルスプロキシは、例えば次のような実装が取りやすいです。
- Azure Functions(HTTP トリガー)で、特定 Blob に
HEAD(または SDK の GetProperties)で確認し、結果に応じて 200/500 を返す - Container Apps / AKS に軽量な API を置き、Storage SDK で疎通チェックして返す
この方式の利点は、Storage 側を「Private Endpoint のみ」「匿名/SAS 無し」のまま維持しつつ、Application Gateway のプローブ要件(2xx)も満たせる点です。追加コンポーネントは増えますが、セキュリティ要件が厳しいほどトータルの説明がしやすい構成になります。
よくあるハマりどころと対処
最後に、現場で頻出する詰まりポイントをまとめます。
| 症状 | 原因のあたり | 対処の方向性 |
|---|---|---|
| Backend health が Unhealthy、応答が見えない | DNS が public に向いている/VNet リンク不足 | Private DNS ゾーンのリンク、DNS フォワーディング、VNet の DNS 設定を見直す |
| 400 が返る | Host ヘッダーが Storage と不一致 | HTTP settings でホスト名上書きを設定し、Storage の FQDN を送る |
| 403/409 が返る | 認証不足(匿名不可のまま) | 静的 Web サイト/専用コンテナーの匿名化/SAS/ヘルスプロキシのいずれかに切り替える |
| HTTPS のみ失敗する | SNI/証明書名の不一致、バックエンドを IP 指定している | ホスト名上書き + SNI、または FQDN 指定に変更する |
ログで確認すると早いポイント
- Application Gateway の バックエンド正常性(Portal)で、失敗理由やステータスを確認する
- 必要に応じて Application Gateway の アクセス ログ/パフォーマンス ログを Log Analytics に出して追う
- Storage 側は 診断設定(Blob のログ)で、プローブが到達しているか・どのステータスを返しているかを見る
まとめ:セキュリティ要件と運用負荷で「落としどころ」を決める
Application Gateway のヘルス プローブで 2xx を返したい場合、最終的には「2xx を返すエンドポイントをどう用意するか」の設計になります。
- 最短で安定:静的 Web サイト($web)にヘルスページを置く
- 匿名範囲を絞る:ヘルスチェック専用コンテナーだけ匿名読み取りにする
- 匿名は不可だが要件は緩められる:SAS で Read 最小権限・短期限・ローテーション
- 匿名も SAS も絶対不可:ヘルスプロキシを用意して認証付きで確認する
「完全閉域 + 認証付きヘルスチェック」は、構成が複雑になりやすい一方で、監査や説明責任の観点では強い選択肢です。逆に、閉域(Private Endpoint + Public network access 無効)を前提にできるなら、ヘルスチェック用の最小限の匿名公開は、運用を大きく楽にしてくれます。自社ポリシーと運用体制に合わせて、無理のないパターンを選んでください。

コメント