Blazor WebAssembly を GitHub Pages にデプロイしたら真っ白になる原因と CORS エラーの完全解説

Blazor WebAssembly を GitHub Pages にデプロイしたら「ローカルでは動くのに本番は真っ白&CORS エラーだらけ」という状態は、設定のポイントさえ押さえればきれいに解消できます。本記事では、実際に遭遇した manifest.webmanifest の CORS エラーと白画面問題を題材に、「なぜそうなるのか」「どう直すのか」を GitHub Pages 向けに具体的な手順とコード例つきで整理します。

目次

Blazor WASM を GitHub Pages に出したらページが真っ白になる問題

今回の相談内容を整理すると、状況は次のようになります。

  • ローカル(IIS Express)では Blazor WebAssembly(スタンドアロン)が正常動作している。
  • 同じ成果物を GitHub Pages に置くと、表示されるのは真っ白なページだけでアプリが立ち上がらない。
  • ブラウザの DevTools(開発者ツール)を見ると、コンソールに CORS エラーが出ている。

代表的なエラーメッセージは次の通りです。

Access to manifest at 'https://www.capitalprints.com/manifest.webmanifest'
from origin 'https://acqu.github.io' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

さらに、画像アップロードに使っていた API(例:imgbb)まわりのエラーも併発しており、axios に書き換えたところ画像アップロードだけは動いたものの、肝心の Blazor アプリ本体は真っ白なまま……という状況です。

この状態を一言でいうと、Blazor が必要とするファイルを「別オリジン」や「配信されないフォルダ」から読みに行ってしまい、ブラウザ側の制約と GitHub Pages の仕様に引っかかっている、ということになります。

CORS エラーと白画面の正体を整理する

まずは、出ている症状とその原因をざっくり表にしてみます。

症状画面の見え方主な原因確認する場所
manifest.webmanifest の CORS エラー画面は真っ白、PWA としても動かない<base href> のせいで別オリジンからマニフェストを取得しようとしているDevTools > Console / Network(Manifest)
_framework/blazor.webassembly.js が 404 / 配信されないアプリ本体が起動せず真っ白GitHub Pages の Jekyll により _framework フォルダが無視されているDevTools > Network(Status 404 or (failed))
API 呼び出しで CORS エラー画面は出るが、API 利用時にエラー呼び出し先サーバが Access-Control-Allow-Origin を返していないDevTools > Network(API リクエストのレスポンスヘッダー)
深い URL に直接アクセスすると 404トップは表示されるが、リロードや直リンクで 404SPA ルーティング用の 404.html が無い / 不十分ブラウザで /counter などに直接アクセス

ポイントは次の 3 つです。

  • CORS は呼び出される側サーバの設定で決まる(フロント側からは基本的に回避できない)。
  • <base href> が GitHub Pages の公開パスとずれていると、すべての相対パスが別オリジンに飛ばされる。
  • GitHub Pages では _framework など「アンダースコア始まりのフォルダ」が Jekyll によって配信されない(.nojekyll が必要)。

今回のケースで実際に起きていること

<base href> が外部ドメインを向いている問題

問題の HTML(index.html)では、次のような記述が入っていたとします。

<base href="https://www.capitalprints.com/" />

この 1 行により、ページ内の すべての相対パス が https://www.capitalprints.com/ を起点として解決されます。つまり、

  • <link rel="manifest" href="manifest.webmanifest">
  • <script src="_framework/blazor.webassembly.js"></script>
  • <link href="css/app.css" rel="stylesheet">

といった記述は、ブラウザから見るとそれぞれ

  • https://www.capitalprints.com/manifest.webmanifest
  • https://www.capitalprints.com/_framework/blazor.webassembly.js
  • https://www.capitalprints.com/css/app.css

として解決されます。

しかし、実際にページを開いている URL は https://acqu.github.io/www.capitalprints.com/ です。つまり、

  • ページ自体のオリジン:https://acqu.github.io
  • リソースを取りに行っているオリジン:https://www.capitalprints.com

という「別オリジン」間通信になっています。その結果、

  • マニフェストは CORS 制約 でブロック。
  • Blazor ランタイム(_framework/*)はそもそも存在しない or 別設定のサーバなので 404。
  • アプリが起動する前にエラーで止まり、画面は真っ白。

という状態になります。

マニフェストの CORS エラーが示していること

Access to manifest at 'https://www.capitalprints.com/manifest.webmanifest'
from origin 'https://acqu.github.io' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

このメッセージを分解すると、次のような意味になります。

  • 今開いているページのオリジンは https://acqu.github.io。
  • ブラウザは https://www.capitalprints.com/manifest.webmanifest を取りに行った。
  • 返ってきたレスポンスに Access-Control-Allow-Origin: https://acqu.github.io(または *)が含まれていない。
  • よって、ブラウザは「CORS ポリシー違反」と判断し、JavaScript からの利用を禁止した。

このエラー自体は PWA(インストール可能な Web アプリ)としての挙動に関わりますが、今回のように <base> が別オリジンを向いていると、マニフェスト以外のファイルも全部外部解決になり、結果としてアプリが起動不能になります。

GitHub Pages と Jekyll による _framework ブロック

Blazor WebAssembly では、ランタイムや DLL、圧縮されたアセットが _framework/ フォルダ以下に配置されます。しかし GitHub Pages では、既定で Jekyll が有効になっており、

  • アンダースコア(_)で始まるフォルダやファイルはサイト生成から除外する

というルールがあります。つまり、何も対策しないと _framework フォルダが配信されず、

  • _framework/blazor.webassembly.js が 404(見つからない)。
  • Blazor アプリの起動前に JavaScript エラーで停止。
  • 画面は真っ白なまま。

という症状になります。

GitHub Pages で Blazor WASM を正常に動かすための設定

ここからは、実際に何をどう直せばよいかを具体的に解説します。ポイントは次の 4 つです。

  • <base href> を GitHub Pages の公開パスに合わせる
  • .nojekyll を置いて _framework を配信できるようにする
  • manifest.webmanifest を同一オリジンから配信する
  • SPA ルーティング用の 404.html を用意する

1. GitHub Pages 用に <base href> を正しく設定する

まず、GitHub Pages の公開 URL を確認します。今回の例では次のような形です。

  • https://acqu.github.io/www.capitalprints.com/

この場合、リポジトリ名が www.capitalprints.com であり、GitHub Pages の公開パスは

  • /www.capitalprints.com/

となります。したがって、index.html の <base href> は次のように修正します。

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <!-- ★ GitHub Pages の公開パスに合わせる -->
  <base href="/www.capitalprints.com/" />

  <!-- マニフェストは相対パスで同一オリジンから取得 -->
  <link rel="manifest" href="manifest.webmanifest" />

  <!-- CSS なども相対パスで OK(base が効く) -->
  <link href="css/app.css" rel="stylesheet" />
</head>
<body>
  <div id="app">Loading...</div>

  <!-- Blazor WASM ランタイム -->
  <script src="_framework/blazor.webassembly.js"></script>
</body>

こうすることで、ブラウザは

  • manifest.webmanifest → https://acqu.github.io/www.capitalprints.com/manifest.webmanifest
  • _framework/blazor.webassembly.js → https://acqu.github.io/www.capitalprints.com/_framework/blazor.webassembly.js

というように、すべてのリソースを GitHub Pages 上の同一オリジンから取得します。CORS を気にする必要がなくなり、あとは GitHub Pages が正しくファイルを配信してくれれば OK です。

なお、将来的に GitHub Pages にカスタムドメイン(例:www.capitalprints.com)を割り当てる場合は、CNAME 設定が反映されたタイミングで <base href="/"> に戻して構いません。

2. .nojekyll を公開ブランチのルートに追加する

次に、Jekyll による _framework フォルダのブロックを回避します。方法は非常にシンプルで、公開ブランチ(通常は gh-pages または main)のルートに、空ファイル .nojekyll を置くだけです。

ローカルで作業している場合は、次のように作成できます。

# プロジェクトの公開用フォルダ(例: dist, docs, wwwroot)に移動
cd path/to/publish

# 空の .nojekyll ファイルを作成(Windows の PowerShell 例)
ni .nojekyll -ItemType File

# GitHub にコミット
git add .nojekyll
git commit -m "Add .nojekyll for Blazor WASM on GitHub Pages"
git push

これにより、GitHub Pages のビルド時に Jekyll が無効になり、_framework を含むすべての静的ファイルがそのまま配信されるようになります。

3. manifest.webmanifest を同一オリジンから配信する

PWA 対応をしている場合、Blazor のテンプレートは wwwroot 直下に manifest.webmanifest を生成します。これをそのまま公開し、HTML からは次のように相対パスで参照します。

<link rel="manifest" href="manifest.webmanifest" />

ポイントは、

  • href に絶対 URL(https://〜)を直書きしないこと
  • <base href> が正しい公開パスを向いていること

の 2 点です。これさえ守れば、マニフェストは常に「今開いているページと同じオリジン」から取得されるので、CORS エラーの心配はありません。

どうしても外部ドメインからマニフェストを配信したい場合は、そのサーバ側で

  • Access-Control-Allow-Origin: https://acqu.github.io

などの CORS ヘッダーを返す必要があります。しかし、多くのケースではそこまで外部に置くメリットはなく、Blazor アプリと同じ場所から配信する方がシンプルです。

4. SPA ルーティング用に 404.html を設置する

Blazor WebAssembly はクライアントサイドルーティング(SPA)を行うため、/counter のような深いパスに直接アクセスしたりリロードしたりすると、GitHub Pages 側では「そんなファイルは無い」と判断し 404 を返してしまいます。

これを防ぐために、SPA 用の 404.html を用意して、すべての 404 アクセスを index.html にリダイレクトします。最小構成の例は次のとおりです。

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="refresh" content="0; url=./index.html" />
  <script>
    // クエリ文字列やフラグメントも引き継いで index.html へ遷移
    location.replace('./index.html' + location.search + location.hash);
  </script>
</head>
<body></body>
</html>

このファイルを公開ブランチのルート(index.html と同じ階層)に置くことで、GitHub Pages 上でのクライアントルーティングが安定します。

5. デプロイ後に開発者ツールで確認すべきポイント

設定を変えたら、必ずブラウザの DevTools で動作を確認しましょう。チェックポイントを一覧にまとめます。

確認内容ツール期待する状態
_framework/blazor.webassembly.js の取得Network タブStatus が 200(404 や (failed) でない)
manifest.webmanifest の取得Network タブ / Application > Manifest同一オリジンから 200 で取得できている
Console のエラーConsole タブCORS エラーや 404 エラーが出ていない
深い URL(例:/counter)への直アクセスブラウザのアドレスバー404 ページではなく Blazor アプリが表示される

外部 API・画像アップロードと CORS の考え方

今回の相談では、画像アップロードに使っている API(例:imgbb)についても「fetch だとエラーだったが axios に変えたら動いた」といった経緯がありました。ここで整理しておきたいのは、

axios に変えたからといって CORS 自体を回避できるわけではない

という点です。

CORS はサーバ側のルールで決まる

CORS(Cross-Origin Resource Sharing)は、ブラウザが「別オリジンへのリクエストを許可してよいかどうか」を判断するための仕組みであり、そのルールは 呼び出される側のサーバがレスポンスヘッダーとして返す値で決まります。

例えば、GitHub Pages 上のページ(https://acqu.github.io)から外部 API(https://api.example.com)にアクセスする場合、API 側が次のようなヘッダーを返していればブラウザはアクセスを許可します。

Access-Control-Allow-Origin: https://acqu.github.io
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With

逆に、このヘッダーが無い・またはオリジンが一致していない場合、ブラウザがリクエストそのものをブロックします。fetch であろうと axios であろうと、この仕組みは変わりません。

axios に書き換えたことで一時的に「動いた」ように見えるのは、

  • デフォルトのヘッダーやリクエスト方式が変わり、プリフライト(OPTIONS)リクエストが発生しなくなった
  • もともと API 側が緩めの CORS 設定になっていた

といった偶然の要素が重なっただけ、という可能性が高いです。根本的な CORS 制約そのものを回避したわけではない、という点は押さえておきましょう。

自分で制御できない API を安全に使うには

第三者の API(例:画像アップロードサービス、外部ストレージなど)をフロントエンドから直接叩く場合、次のいずれかの対策が現実的です。

  • API 提供元が公式にサポートしている CORS 設定(ドキュメント)を確認し、その範囲で利用する。
  • どうしても CORS が厳しくて使えない場合は、自前のプロキシサーバを用意してサーバサイドから API を叩く。

プロキシのイメージとして、Cloudflare Workers での簡易実装例を示します。

export default {
  async fetch(req) {
    const url = new URL(req.url);
    const target = 'https://api.example.com' + url.pathname + url.search;

    const resp = await fetch(target, {
      method: req.method,
      body: req.body,
      headers: req.headers
    });

    const h = new Headers(resp.headers);
    h.set('Access-Control-Allow-Origin', 'https://acqu.github.io');
    h.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    h.set('Access-Control-Allow-Headers', '*');

    if (req.method === 'OPTIONS') {
      return new Response(null, { headers: h });
    }

    return new Response(resp.body, { status: resp.status, headers: h });
  }
}

このようなプロキシを挟むことで、

  • ブラウザ → プロキシ(同一オリジン or 許可済みオリジン)
  • プロキシ → 外部 API(サーバサイド)

という形に分離でき、ブラウザ側の CORS 制約をコントロールしやすくなります。ただし、API キーの管理や利用制限など、セキュリティ面の考慮も必要です。

実際の修正手順をステップバイステップで整理

ここまでの内容を踏まえて、実際に GitHub Pages 上で Blazor WASM を動かすための手順をまとめます。

ステップ 1:Blazor アプリをビルド・発行する

  • Visual Studio または CLI で、Blazor WebAssembly(スタンドアロン)アプリを Release ビルド。
  • dotnet publish -c Release -o ./publish のように、発行フォルダを作成。
  • 発行先フォルダには index.html, _framework, wwwroot などがまとまっている状態にする。

ステップ 2:index.html の <base href> と manifest の参照を修正

  • publish/index.html を開く。
  • <base href="/"> などになっている部分を、GitHub Pages の公開パスに合わせて変更。

例:https://acqu.github.io/www.capitalprints.com/ で公開する場合

<base href="/www.capitalprints.com/" />
  • <link rel="manifest" href="manifest.webmanifest"> のように、マニフェストは相対パス参照にしておく。

ステップ 3:.nojekyll と 404.html を追加

  • 発行フォルダ(GitHub にアップするフォルダ)のルートに .nojekyll を作成。
  • 同じくルートに、先ほど紹介した簡易リダイレクトの 404.html を配置。

ステップ 4:GitHub Pages 用のブランチにアップロード

  • 発行フォルダの中身を、GitHub のリポジトリ(例:gh-pages ブランチ)にコミット。
  • GitHub のリポジトリ設定 > Pages で、公開対象ブランチとフォルダ(root または /docs)を指定。

ステップ 5:公開 URL にアクセスして動作確認

  • 公開 URL(例:https://acqu.github.io/www.capitalprints.com/)にアクセス。
  • DevTools を開き、Network / Console を見ながら次を確認:
  • _framework/blazor.webassembly.js が 200 で取得できている。
  • manifest.webmanifest が CORS エラーなしで取得されている。
  • API 呼び出し時に CORS エラーが出ていない。
  • /counter などのパスに直接アクセスしても 404 ではなくアプリが表示される。

再発防止用チェックリスト

最後に、同じ問題でハマらないためのチェックリストを整理しておきます。デプロイのたびにざっと確認すると安心です。

  • [ ] index.html の <base href> が GitHub Pages の公開パス(例:/www.capitalprints.com/)になっている。
  • [ ] マニフェスト・CSS・JS を絶対 URL ではなく相対パスで参照している。
  • [ ] 公開ブランチのルートに .nojekyll が置いてある。
  • [ ] 404.html を配置し、深い URL 直アクセスでも Blazor アプリに戻るようになっている。
  • [ ] Network タブで _framework/, _content/ フォルダ配下の静的ファイルが 200 で配信されている。
  • [ ] 外部 API を利用している場合、そのドメインに対する CORS 設定(またはプロキシ)が用意されている。
  • [ ] カスタムドメインを設定する場合、CNAME 設定後に <base href="/"> に変更する手順を把握している。

よくある誤解・ハマりポイントまとめ

  • 「axios に変えれば CORS を回避できる」
    → できません。CORS はサーバ側のレスポンスヘッダーで決まるため、クライアントライブラリで魔法のように無効化することはできません。
  • 「GitHub Pages に上げたら勝手にうまくやってくれる」
    → Blazor WASM のように _framework フォルダを使うアプリでは、.nojekyll と <base href> の調整がほぼ必須です。
  • 「PWA 用のマニフェストは別ドメインから配信しても大丈夫」
    → 理論上可能ですが、CORS 設定が必要になり、トラブルの原因になりがちです。基本はアプリと同じオリジンで配信しましょう。
  • 「API キーをそのまま JavaScript に埋め込んでも問題ない」
    → 公開リポジトリにコミットされたキーは誰でも閲覧できます。利用制限(Referer 制限や IP 制限)をかけるか、サーバ側で隠す設計を検討しましょう。
  • 「ローカルで動いているから本番も大丈夫」
    → ローカルでは同一オリジン扱いになってしまうケースが多く、本番の CORS やパスの問題が見えにくくなります。できればローカルでも GitHub Pages と似たパス構成でテストすると安心です。

まとめ:Blazor WASM × GitHub Pages の安定デプロイパターン

Blazor WebAssembly アプリを GitHub Pages に公開する際、

  • <base href> を公開パスに合わせる
  • .nojekyll を配置して _framework フォルダを配信可能にする
  • manifest.webmanifest を同一オリジンから取得する
  • SPA 用の 404.html を用意する

という 4 点を押さえておけば、「ローカルでは動くのに GitHub Pages だと真っ白」「CORS エラーが大量発生」という状態はほぼ避けられます。外部 API を利用する場合も、CORS はサーバ側の設定であることを理解し、プロキシの活用や API キーの扱いを含めて設計することが重要です。

一度このパターンをテンプレートとして固めておけば、以降の Blazor プロジェクトでもほぼコピペで安定した GitHub Pages デプロイが実現できます。今回の手順をベースに、自分のプロジェクト向けにチェックリストやスクリプトを整備しておくと、今後の運用がぐっと楽になるはずです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次