「Azure Static Web Apps を無料プランから有料プランへ切り替えたら、*.azurestaticapps.net とカスタムドメインのどちらでも Azure 既定の 404 ページしか出ない」——この症状は、デプロイ失敗・ルーティング設定漏れ・DNS/証明書/キャッシュの遅延が重なったときに起きやすい代表例です。本記事では、最短で復旧するための切り分け手順、staticwebapp.config.json の正しい書き方、DNS と証明書の再検証、よくある落とし穴と再発防止策までを一気通貫で解説します。
現象の整理とゴール
- 無料プランの利用をやめる/有料プランへ切替後、ポータルの「既定ホスト名(
*.azurestaticapps.net)」とカスタムドメインの両方で 404(Azure 既定ページ)が表示される。 - 再デプロイ(CI/CD の再実行)を行っても変化がない。
- ゴール:本番(Production)環境で
/および任意の SPA ルート(例:/about)が 200 で配信される状態に復旧。
まず結論:原因は大きく 6 系統
| 系統 | 代表的な原因 | 最優先チェック |
|---|---|---|
| デプロイ | ビルド失敗/output_location の誤り/成果物が空 | ポータルの Deployment で最新が Success か、GitHub Actions/DevOps のログで出力先パスを確認 |
| ルーティング | SPA なのに navigationFallback がない/配置場所が不正 | 公開ルート直下に staticwebapp.config.json があるか、中身が期待通りか |
| ドメイン/DNS | CNAME の向き先誤り/DNS 伝播遅延/Apex の設定ミス | カスタムドメイン設定の状態(検証済み/証明書発行済み)と CNAME 解決結果 |
| 証明書 | SSL/TLS の発行保留/期限切れ/バインド不備 | ポータルのドメイン詳細で証明書が 有効 か、更新中か |
| サブスクリプション | プラン切替の反映遅延/リソース違いに CNAME が残存 | 課金・サブスクリプションの警告、SWA リソース名と CNAME 参照先の照合 |
| キャッシュ | エッジ/ブラウザ/CDN のキャッシュに 404 が残留 | 時間をおく・ブラウザ/エッジを明示パージ・Cache-Control の見直し |
最短復旧のためのチェックリスト(即実行)
- ポータルでデプロイ結果を確認:Static Web App → Deployment の最新ジョブが Success であること。失敗ならビルドログのエラー行を修正し再実行。
- ビルド出力のパスを検証:GitHub Actions/DevOps の設定で
app_location/output_locationがフレームワークの出力(例:React はbuild、Angular はdist/<app>)と一致しているか。 staticwebapp.config.jsonを公開ルートに配置:SPA ならnavigationFallbackを必ず設定。配置は「公開されるルートの直下」(=output_locationの直下)。- カスタムドメインの CNAME と証明書:CNAME が
<SWA名>.azurestaticapps.netを向き、ポータルの状態が「検証済み」「証明書有効」であること。 - サブスクリプション/プラン状態:ポータルの通知(ベル)に課金・制限警告がないか。有料化に伴い別リソースを新規作成していないかを確認(CNAME の参照先が旧リソースだと 404)。
- キャッシュのクリア:CDN(利用時)の Purge、ブラウザの強制再読込/キャッシュ削除。DNS は 24 時間程度の伝播ラグを見込む。
質問のケースに対する模範解答(要点まとめ)
| 主な対処 | 内容 | 補足・ポイント |
|---|---|---|
| デプロイログの確認 | Azure Portal → Static Web App → Deployment で最新デプロイが “Success” か確認。 | 失敗時はビルドログのエラーを直し再デプロイ。 |
staticwebapp.config.json を用意 | navigationFallback で「すべてのリクエストを /index.html へ書き換え」。 | SPA では必須。配置場所/ファイル名のタイプミスに注意。 |
| カスタムドメイン設定を再確認 | CNAME が <SWA名>.azurestaticapps.net を向いているか、SSL 証明書が有効か。 | DNS 変更は世界中へ浸透するまで最大 24 時間。 |
| サブスクリプション状態を確認 | ポータルの通知に警告がないか、プランが正しく切替わっているか。 | 無料→有料直後は反映ラグがあり得る。 |
| キャッシュの影響を疑う | CDN/ブラウザに古い 404 が残留することがある。 | 時間をおく、CDN を Purge、ブラウザキャッシュ削除。 |
| カスタムエラーページ | responseOverrides で独自 404 を用意。 | 原因切り分けが容易になる。 |
診断のゴールデンパス(コピペ手順)
1. GitHub Actions / Azure DevOps のワークフロー確認
# 例: GitHub Actions のステップ抜粋
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- uses: Azure/static-web-apps-deploy@v1
with:
app_location: "/"
output_location: "build" # React の既定
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
output_locationが空/誤りだと成果物が配信ルートに現れず 404。- モノレポの場合は
app_locationの相対パスがずれていないかを確認。
2. SPA のルーティングを必ず有効化
React / Angular / Vue / Next.js(SPA モード)では、既存のファイルと一致しない任意のパス(例:/dashboard)が 404 にならないよう、navigationFallback で /index.html へリライトします。
{
"navigationFallback": {
"rewrite": "/index.html"
}
}
配置先は「公開ルートの直下」です。たとえば output_location: build なら build/staticwebapp.config.json として成果物に含まれている必要があります(=リポジトリ直下に置くだけでは不可)。
3. 独自 404 ページで切り分けを容易に
{
"responseOverrides": {
"404": { "redirect": "/404.html", "statusCode": 404 }
}
}
この設定を入れておくと「SWA 既定 404」か「自前 404」かでトラブルの層がわかります。自前 404 が出る=SWA のルーティングまでは通っている合図です。
4. ドメインと証明書の健全性チェック
- ポータルのカスタムドメイン一覧で状態が「検証済み」かつ「証明書:有効」になっているか。
- CNAME が
<SWA名>.azurestaticapps.netを向いているか。Apex ドメインの場合は DNS 事業者の ALIAS/ANAME 等の仕組みを使用して CNAME 同等の挙動にする。 - DNS 変更直後は地域差のある伝播遅延が発生するため、最低でも数時間〜24 時間は様子を見る。
よくある落とし穴と実例
ビルド成果物の空配信
ワークフローの npm run build を失敗したまま deploy ステップが走ると、SWA 側には空のルートがデプロイされ、どのパスも 404 になります。ログ末尾だけでなく、Build セクションの警告・エラーを確認してください。
staticwebapp.config.json の配置場所が違う
多いのは、src/ やレポジトリ直下に置いているケースです。SWA は「公開される成果物の直下」にあるファイルだけを読み取ります。ビルドでコピーされるよう調整しましょう(例:cp staticwebapp.config.json build/)。
Next.js のエクスポートと動的ルート
Next.js を静的エクスポートしているのに、動的ルートへのビルド時生成が漏れていると 404 になります。ISR/SSG の生成対象、trailingSlash、basePath を含めて出力と output_location を突き合わせてください。
API パス(/api)が 404
Functions 連携を有効化している場合、api_location の指定ミスや Functions のビルド失敗で /api/* が 404 になります。routes で /api を navigationFallback の対象から除外するのも基本です。
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/api/*"]
}
}
HTTP レベルでの切り分け
アプリ側か配信基盤かを見極めるには、HTTP 応答の特徴を観察します。
| 観察点 | 正常(200 の想定) | 既定 404 のとき | 示唆される原因 |
|---|---|---|---|
既定ホスト名での /index.html | 200 | 404 | 配信ルートにファイルがない/成果物未配置 |
既定ホスト名での / | 200 | 404 | navigationFallback 不足 |
カスタムドメインでの /index.html | 200 | 404 | CNAME/証明書/伝播遅延(ドメイン層) |
/api/health(ある場合) | 200 | 404 | Functions 未デプロイ/API 位置の誤り |
Azure ポータルでの要チェック項目(画面別)
Overview
- Default domain(
*.azurestaticapps.net)にアクセスして 200 を確認。 - Resource group / Location が想定と一致(別リージョンの新規リソースを作ってしまい CNAME が旧リソースを向いている事例が多発)。
Deployment
- 最新のデプロイが Success。前回との差分(コミット、ブランチ)も確認。
- ビルドログで
output_locationの完全パスと実ファイル数(Copied N files 等)を確認。
Custom domains
- 各ドメインの状態が Verified / Certificate: Valid。
- 失効や保留中(Pending)なら時間を置くか、ドメイン側のレコード/所有確認を再実施。
CLI でのヘルスチェック例(任意)
# ログイン
az login
# SWA の概要
az staticwebapp show -n -g
# ドメイン一覧
az staticwebapp hostname list -n -g
# 環境(Production/Preview)
az staticwebapp environment list -n -g
これらで「参照しているリソース」「バインド済みドメイン」「有効な環境」を目視できます。プラン切替時に別名・別リージョンで作った新リソースに CNAME を向け忘れるミスを素早く検出できます。
ケーススタディ:何もしていないのに直った?
質問の背景にもある通り、「何も変更せず自然復旧」することがあります。多くは DNS 伝播・証明書発行・エッジキャッシュの更新が裏で完了した結果です。再現性が低い一方で、本番の可用性 という観点ではタイムアウトを短くし、監視と通知を整備して「待てる状況」をつくるのが重要です。次項のベストプラクティスを適用してください。
再発防止のベストプラクティス
- キャッシュ戦略:HTML には短い
Cache-Control、静的アセットには長いmax-ageとimmutableを付与。デプロイ失敗時の 404 キャッシュ残留を最小化。 - DNS TTL の見直し:切替期は TTL を一時的に短くし、安定後に戻す。
- ヘルスエンドポイント:
/healthzのような静的 200 を置き、外形監視で 3 地域以上からチェック。 - ワークフローの成否ゲート:build が失敗したら deploy を実行しないようにする(
if: success()等)。 - 環境の明確化:Preview 環境の URL と Production を明示的に区別。誤って Preview の成果物を参照していないかを日常的に確認。
- Infra as Code:ドメイン追加・証明書・ルーティングをスクリプト化(Bicep/ARM/Terraform)。人手の設定ブレを排除。
トラブル別の対処レシピ
デプロイが Success なのに 404
output_locationとビルド成果物(例:build/index.html)の実在をログで確認。- 既定ホスト名で
/index.htmlに直アクセスして 200 か確認。 /だけ 404 ならnavigationFallbackを追加。
カスタムドメインだけ 404
- CNAME が正しい SWA の既定ホスト名を向いているか。
- ポータルでドメイン状態が検証済み/証明書有効か。
- DNS 伝播を考慮して時間を置くか、DNS キャッシュをフラッシュ。
無料→有料切替後に 404
- 既存リソースのプラン切替か、新規リソース作成かを整理。CNAME が旧リソースを向いていないか。
- 課金確定までの短いラグはあり得るため、ログとポータルの状態を随時確認。
- 疑わしい場合は「強制再デプロイ」(空コミットや再実行)で最新成果物を明示配信。
完全版 staticwebapp.config.json サンプル
一般的な SPA + API 連携の構成例です。必要に応じてカスタマイズしてください。
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/assets/*", "/images/*", "/api/*", "/favicon.ico", "/robots.txt"]
},
"globalHeaders": {
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "strict-origin-when-cross-origin"
},
"mimeTypes": {
".json": "application/json; charset=utf-8"
},
"responseOverrides": {
"404": { "redirect": "/404.html", "statusCode": 404 },
"401": { "redirect": "/signin.html", "statusCode": 302 }
}
}
ポイント:
- exclude に
/api/*を入れて SPA が API パスを誤って飲み込まないようにする。 - 静的アセット(
/assets/*等)はファイル実体があるため Fallback から除外。 globalHeadersでセキュリティヘッダも同時に整えると一石二鳥。
デバッグに役立つコマンド
| 目的 | コマンド例 | 期待値 |
|---|---|---|
| DNS 解決 | nslookup www.example.com | CNAME が xxxx.azurestaticapps.net を返す |
| HTTP ステータス | curl -I https://<既定ホスト名>/index.html | HTTP/2 200 |
| キャッシュ無視で取得 | curl -H "Cache-Control: no-cache" -I https://www.example.com/ | 最新の応答(404 が消えていればOK) |
チェックリスト(保存版)
- 最新デプロイが Success。
- 成果物に
index.htmlが含まれている(サイズ > 0)。 staticwebapp.config.jsonが公開ルート直下に存在。navigationFallbackが設定され、API/アセットはexclude。- カスタムドメインは Verified、証明書は Valid。
- CNAME は正しい SWA 既定ホスト名を向く。
- サブスクリプションの警告無し、プランは期待通り。
- ブラウザ/エッジ/CDN キャッシュをクリア済み。
- 既定ホスト名で
/index.htmlが 200。 - カスタム 404 を用意(
responseOverrides)。 - 外形監視で 200 を複数地域から確認。
FAQ
Q. 既定ホスト名では 200、カスタムドメインだけ 404 です。
A. ドメイン/DNS/証明書レイヤーの問題が濃厚です。CNAME の向き先、ポータルの証明書状態、DNS 伝播を確認し、必要なら時間を置いて再試行してください。
Q. /index.html は 200 ですが、/ や /about が 404 です。
A. navigationFallback が未設定です。上記のサンプルを配置して再デプロイしてください。
Q. 有料化後、プレビュー環境の URL は開けるのに本番だけ 404 です。
A. Production 環境のデプロイが空になっている可能性があります。環境タブで Production を選び、最新の成功デプロイがあるか確認してください。
まとめ
Azure Static Web Apps の 404 は、デプロイ成果物の欠落、SPA のルーティング設定漏れ、ドメイン/証明書/DNS 伝播、そして キャッシュ残留のいずれか(または複合)で説明できます。ポータルの Deployment → Custom domains → プラン/通知の順で確認し、問題が見つからなければキャッシュと伝播の待機に切り替えるのが最短復旧の定石です。staticwebapp.config.json の基本形を押さえ、デプロイ時に公開ルートへ確実に含める運用を整えれば、同種のトラブルは大幅に減らせます。
付録:実務でそのまま使えるテンプレ
GitHub Actions(React の例)
name: Deploy SWA
on:
push:
branches: [ main ]
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- uses: Azure/static-web-apps-deploy@v1
with:
app_location: "/"
output_location: "build"
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
staticwebapp.config.json(最小構成)
{
"navigationFallback": { "rewrite": "/index.html" }
}
CDN/キャッシュの方針(例)
# HTML は短命キャッシュ
Cache-Control: no-cache, no-store, must-revalidate
# 指紋付きアセットは長命
Cache-Control: public, max-age=31536000, immutable
ダウntime時の応急措置
- 既定ホスト名が正常なら、一時的に CNAME を既定ホスト名に戻す/向け直す。
- ステータスページ(静的 200)への手動切替を用意。
- デプロイをロールバック(直近の成功コミットに戻す)。

コメント