Azure App ServiceでBlazorのログインAPIが500になる原因と解決策|/api/auth/signinのルーティング競合を解消

Azure App Service に Blazor アプリを公開したら、画面は表示されるのにログインだけが 500 で失敗する――そんなときは「認証」より先に「ルーティング」を疑うのが近道です。本記事では /api/auth/signin が 500 になる典型パターンと、原因の切り分け・修正手順を具体例つきで解説します。

目次

Azure App Service の Blazor で /api/auth/signin が 500 になる現象

Azure App Service 上では Blazor アプリ自体は正常に起動し、トップページや画面遷移もできるのに、ログイン処理(サインイン)だけが失敗するケースがあります。典型的には、ブラウザ側の login.js から次の API を呼んだ瞬間にエラーが発生します。

  • POST /api/auth/signin が 500 Internal Server Error
  • Postman などの API クライアントから叩いても同様に 500(=フロントの実装のせいではない)
  • ローカル(開発 PC)では動いていたのに、App Service でだけ再現する

このトラブルは、Blazor WebAssembly(Hosted)でも Blazor Server でも、「サーバー側に ASP.NET Core の Web API(Controller や Minimal API)を持っている構成」であれば発生し得ます。特に /api/* 配下を複数プロジェクト・複数方式で扱っている場合は要注意です。

項目ローカル環境Azure App Service
Blazor アプリの起動問題なし問題なし
POST /api/auth/signin200 / 204 など成功500(ログイン不可)
発生箇所の見え方デバッガで例外に気づける画面側は「ログインに失敗」程度の表示で終わりがち

レスポンス本文は、実装によっては次のような「ざっくりした JSON」で返ることがあります(グローバル例外ハンドラ等でラップしている場合)。

{"error":"Internal server error","details":"Login failed for user '<token-identified principal>'."}

この手の 500 は「認証が悪い」「DB が悪い」と思い込みやすいのですが、実際には API のルーティング競合 が原因になっていることがあります。ここを最初に疑えると、調査時間を大きく短縮できます。

まずやる切り分け:500 はクライアントではなくサーバー側の問題

前提として、HTTP 500 は「サーバー側で例外が起きた」ことを示します。JavaScript や Blazor 側の UI が原因で 500 になることは基本的にありません(UI は 400/401/403 を引き起こすことはあっても、500 を作るのはサーバー側です)。

最初の 10 分でやっておくと良い切り分けは次の 2 つです。

  1. ブラウザの DevTools(Network タブ)で /api/auth/signin のステータスとレスポンスを確認
  2. Postman / curl で同じ API を叩いて、フロント抜きで 500 が再現するか確認

Postman でも 500 が返るなら、フロントの不具合ではなく、API 側(ASP.NET Core)の実行経路に問題があると判断できます。

原因特定を早める:Azure App Service で例外ログを最短で見る

App Service で 500 が出たら、まず例外ログ(スタックトレース)を取りに行きます。ここが見えないと、推測で迷子になりがちです。

確認先の優先順位

確認先おすすめ度見えるものポイント
Log stream高リアルタイムログ再現操作しながら追える。まずはここ。
Application Insights高例外・依存関係・リクエストトレース本番運用でも残せる。原因分析が最も速い。
診断ログ(App Service logs)中ファイル / ストレージに出るログ後追い調査に強い。ログの保存期間に注意。

ログを確認するための最低限の手順

  • Azure Portal で対象の App Service を開き、Log stream を表示した状態でログイン操作を再現する
  • Application Insights を使っている場合は、Requests / Failures / Exceptions から /api/auth/signin を検索し、関連する例外(スタックトレース)まで辿る
  • 情報が足りないときは、アプリ設定で一時的にログレベルを上げて「ルーティング周りのログ」が出るようにする(例:Logging__LogLevel__Microsoft.AspNetCore.Routing)

本番でログレベルを上げる場合は、機密情報が出ないか・ログ量が増えすぎないかに注意し、原因が取れたら元に戻す運用がおすすめです。

ログを開いたときに注目するのは「どの例外が」「どのエンドポイントで」「どの順番で起きたか」です。今回のケースでは、ログイン処理そのものより前段の ルーティング で例外が出ていることがあります。

結論:[ApiController] と [Route("api/auth")] の重複がルーティング競合を起こしていた

今回の根本原因は、api/auth を担当する Controller(または同等のエンドポイント)が複数存在し、同じ URL が複数のハンドラに一致してしまう状態になっていたことです。

ASP.NET Core の属性ルーティングでは、次のように [Route] と [HttpPost] 等の組み合わせで URL パターンが決まります。

  • Controller に [Route("api/auth")]
  • Action に [HttpPost("signin")]
  • 合成されて /api/auth/signin になる

ところが、同じ合成結果になる Action が 2 つ以上あると、実行時に「どちらを呼ぶべきか」が決められず、ルーティングが曖昧(Ambiguous)になって 500 が返ることがあります。エラーハンドリングの実装次第では、曖昧さの例外が握りつぶされて「内部エラー」として見えてしまうため、認証や DB のエラーに見えることもあります。

ログに出やすいキーワード:Ambiguous / matched multiple endpoints

ルーティング競合が原因の場合、App Service の Log stream や Application Insights の例外に、次のような文言が含まれることが多いです(表現は .NET のバージョンやホスティング方式で多少変わります)。

AmbiguousMatchException: The request matched multiple endpoints.
Matches:
  .../api/auth/signin (AuthController.SignIn)
  .../api/auth/signin (AccountController.SignIn)

この「matched multiple endpoints(複数のエンドポイントに一致した)」が見えたら、認証や DB を疑う前に、同一ルートが二重定義されていないかを点検するのが最短ルートです。

やりがちな重複パターン

典型例は「似た責務の Controller を増やした」「リファクタ途中で古い Controller が残った」「Minimal API と Controller の両方で同じルートを定義した」といったパターンです。

重複の形例起こり得ること
Controller 同士が重複AuthController と AccountController が両方 [Route("api/auth")]同一 URL に複数 Action がマッチ
Minimal API と Controller が重複app.MapPost("/api/auth/signin", ...) と Controller の [HttpPost("signin")]環境やビルド構成で挙動差が出やすい
部分一致・既定ルートの混在属性ルートと MapControllerRoute の設計が衝突ある URL だけ予期せず別 Action に到達

問題のあるコード例(重複している状態)

例えば次のように、2 つの Controller が同じ api/auth を名乗ってしまうと、POST /api/auth/signin は二重定義になります。

// 例:AuthController
[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    [HttpPost("signin")]
    public IActionResult SignIn([FromBody] SignInRequest request)
    {
        // ログイン処理...
        return Ok();
    }
}

// 例:AccountController(意図せず同じルートになっている)
[ApiController]
[Route("api/auth")]
public class AccountController : ControllerBase
{
    [HttpPost("signin")]
    public IActionResult SignIn([FromBody] SignInRequest request)
    {
        // 古い実装 / 別実装...
        return Ok();
    }
}

ローカルでは「たまたま片方に流れている」ように見えることがありますが、本番(App Service)ではアセンブリの読み込み順・最適化・公開方法などの差で表に出ることがあります。重要なのは、設計として URL が一意になっていない時点で、いつ破綻してもおかしくないという点です。

解決策:api/auth を担当するルート定義を 1 つに統一する

対処はシンプルで、api/auth を扱う Controller(または同等のエンドポイント)を 1 つに決めて、重複を無くすことです。取り得る手段は大きく 2 つあります。

重複している [ApiController] / [Route("api/auth")] を片方から削除する

「実際に使うのは片方だけ」なら、不要な Controller を削除するか、少なくともルート属性を外してルーティング対象から外します。プロジェクトの履歴上、古い Controller が残りやすいので、思い切って整理するのが安全です。

// 不要な Controller を削除できない事情がある場合は、ルートを外す例
// (ただしこの Controller を API として公開しない前提)

// [ApiController]  ←削除
// [Route("api/auth")] ←削除
public class AccountController : ControllerBase
{
    // ...
}

片方のルートを別のパスに変更して衝突を避ける

両方とも必要な場合は、責務が分かるようにパスを分けます。例えば「認証」と「アカウント管理」を分離したいなら、次のように URL を設計します。

目的例:推奨パス理由
サインイン/サインアウトなど認証/api/auth/*認証の責務が明確
ユーザー情報、プロフィール更新/api/account/*アカウント管理と分離できる
管理者向け操作/api/admin/*権限制御・監査ログの設計がしやすい
[ApiController]
[Route("api/account")]
public class AccountController : ControllerBase
{
    [HttpPost("signin")] // ←「signin」という Action 名は同じでも、合成 URL が変わるので衝突しない
    public IActionResult SignIn([FromBody] SignInRequest request)
    {
        return Ok();
    }
}

このどちらかでルーティング競合が解消され、Azure App Service 上でも POST /api/auth/signin が正常に動作するようになります。

修正後に必ず確認すること:エンドポイントが一意になったか

修正したら、次の 3 点をセットで確認します。ここまでやると、再発の可能性がぐっと下がります。

  • ソース全体検索で [Route("api/auth")] が 1 箇所だけになっているか
  • 同じく "/api/auth/signin" を文字列検索し、Minimal API 等での重複が無いか
  • App Service に再デプロイ後、Postman で 200/204 を確認してから UI 側のログインを試す

「Controller は 1 つなのに直らない」場合は、Minimal API、Razor Pages、gRPC など別の経路で同じパスが定義されていないかを疑ってください。

補足:ローカルでは見逃しやすい理由と、現場で効くチェック順

ルート重複は、ローカル環境だと見逃しやすい代表格です。理由はいくつかあります。

  • アクセスしていない経路は気づかない:開発中にログインを毎回同じ手順で試していると、偶然「片方」に当たり続けることがある
  • デバッグ時の例外表示が親切:ローカルは開発用例外ページが出て原因がすぐ分かるが、本番は一般化された 500 だけが返る
  • 公開方法で構成が変わる:不要な Controller がトリミングされる/されない、アセンブリが追加される等で、実行時のエンドポイント集合が変わることがある

そのため、実務では次の順番で確認すると迷いにくいです。

  1. まず App Service のログで「ルーティングの曖昧さ」を示す例外がないか見る
  2. 次に [Route] の重複をソース検索で潰す(特に api/auth 配下)
  3. 最後に認証(Cookie/JWT/OIDC)や DB 接続の確認に進む

再発防止:ルーティング競合を作らない設計ルール

「直す」だけで終わらせず、同じ事故を防ぐためのルールを決めておくと効果的です。チーム開発や長期運用の Blazor アプリほど、ここが効いてきます。

ルール具体例狙い
API の責務ごとにプレフィックスを固定する/api/auth は認証だけ、/api/account はアカウントだけ「似た Controller」が増えたときの衝突を防ぐ
同一パスを複数の方式で定義しない同じ URL を Minimal API と Controller の両方で持たない環境差・将来の拡張での破綻を防ぐ
命名で責務が分かるようにするAuthController / AccountController を曖昧にしないレビューで気づける確率を上げる
CI で「重複ルート」を検知する起動テストでエンドポイント一覧を出し、同一パスを検出本番事故を事前に止める

簡易的に「重複ルート」を洗い出すコツ

コードレビューや静的解析だけでは漏れることがあるので、次のような「機械的な確認」を組み合わせると強いです。

  • ソリューション全体で [Route("api/ を検索し、同じプレフィックスが複数 Controller に出ていないか確認
  • dotnet publish で本番と近い成果物を作り、ローカルで起動して API を叩く(ローカル実行=常に本番と同じではないため)
  • Application Insights を有効化して、例外が「握りつぶされず」に残る状態を作る

よくある質問

ルートが重複すると、必ず 500 になりますか?

同じ HTTP メソッドで同じ URL に一致するエンドポイントが複数ある場合は、原理的に「どれを選ぶか」を決められないため、実行時エラー(結果として 500)になりやすいです。重複の程度やフレームワークの選択規則によっては、たまたま片方に寄って見えることもありますが、設計として不安定なので早めの解消が安全です。

[ApiController] を外すだけでも直りますか?

状況によります。[ApiController] はモデルバインディングや自動 400 応答など、Web API として便利な機能を有効にします。ルーティング競合そのものを解消するには、最終的に「同じ URL を複数が担当しない」状態にする必要があります。[ApiController] を外すのは「API として公開しない Controller」から外す、という意味では有効ですが、無理に外して挙動を変えるより、ルート設計を整理する方が本筋です。

なぜローカルで動いて、Azure でだけ壊れたのですか?

環境差があると「たまたま動いていた」が表面化します。公開時のビルド最適化、読み込まれるアセンブリ、設定ファイル、ミドルウェアの順序などで、最終的に生成されるエンドポイント集合やマッチング順が変わることがあります。ローカルで動作していても、ルート定義が一意でない場合は本番で破綻しやすいので、Azure 上の例外ログと合わせて、まずルーティングを疑うのが有効です。

同じ /api/auth/signin でも、片方が [Authorize] 付きなら共存できますか?

基本的にはおすすめできません。認可属性の有無でエンドポイントの選択が分岐するわけではないため、同じパスが複数ある時点で曖昧さが残ります。認可ポリシーで分けたいなら、URL を分ける(例:/api/auth/signin と /api/admin/signin)か、同一 Action 内で条件分岐する方が設計が明確です。

まとめ:ログイン 500 の近道は「認証」より先に「ルーティング」を疑うこと

  • Azure App Service で POST /api/auth/signin が 500 のとき、まずはログ(Log stream / Application Insights)で例外を確認する
  • [ApiController] + [Route("api/auth")] の重複などで、同じ URL を複数の Controller/エンドポイントが担当しているとルーティング競合で 500 になり得る
  • 対処は「api/auth を 1 つに統一」するか「別パスに分割」すること。修正後はソース検索と Postman で一意性を必ず確認する

Blazor アプリのログイン障害は、つい認証設定やトークン、DB 接続に意識が向きがちです。しかし、URL を誰が担当するかが曖昧な状態は、環境が変わった瞬間に顕在化します。今回のように api/auth のルートを整理するだけで解決するケースもあるため、まずはルーティングの一意性を点検してみてください。

この記事を書いた人

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

コメント

コメントする

目次