Azure AD(Microsoft Entra ID)でアプリ認証を実装していると、「本番はうまくいくのにローカル開発だけリダイレクトURIでハマる」という相談が非常に多いです。特に app.localhost のような localhost サブドメインを使いたいケースでは、なぜか Azure AD に登録できず、AADSTS500117 エラーで足止めされがちです。この記事では、その理由と実運用で使えるワークアラウンド、そして「ローカルも HTTPS」に寄せるための具体的な構成例を解説します。
Azure AD / Microsoft Entra ID とリダイレクトURIの基本
まず前提として、Azure AD(現 Microsoft Entra ID)のアプリ登録では、OAuth 2.0 / OpenID Connect の「リダイレクトURI(Reply URL)」を厳密にチェックします。
- スキーム(
https:///http://) - ホスト名(例:
app.ourdomain.com) - ポート番号(例:
:3000) - パス(例:
/auth/callback)
これらが事前に登録したものと完全一致しないと、Azure AD はセキュリティの観点からリダイレクトを拒否し、代表的には次のようなエラーを返します。
AADSTS500117: The reply uri specified in the request isn't using a secure scheme.
本番環境では https://app.ourdomain.com や https://clientxyz.ourdomain.com のような HTTPS のサブドメインを問題なく登録できますが、ローカル開発になると事情が変わります。
ローカル開発でやりたいこと:localhost サブドメインの利用
モダンなSaaSやマルチテナントアプリでは、テナントごとにサブドメインを変える構成が一般的です。
- 本番例
https://app.ourdomain.comhttps://clientxyz.ourdomain.com
- 開発環境で模倣したい構成の例
http://app.localhost:3000http://clientxyz.localhost:3000
このように、ローカルでも *.localhost を使ってサブドメイン構成を完全に再現したい、というニーズは非常に強いです。テナント判別ロジック、ルーティング、Cookie のスコープなど、本番と同じドメイン設計でテストできるのが理想だからです。
しかし、ここで Azure AD の仕様にぶつかります。
Azure AD が許可する HTTP リダイレクトURIの範囲
Azure AD が HTTP(暗号化されていない通信)を許可するのは、例外的に http://localhost 系のみです。具体的な挙動を表にまとめると次のようになります。
| リダイレクトURI | 登録可否 | 備考 |
|---|---|---|
http://localhost | 許可 | ポート無し |
http://localhost:3000 | 許可 | ポート指定付きでもOK |
http://127.0.0.1:3000 | 不可 | IP指定は HTTP では拒否される |
http://app.localhost:3000 | 不可 | AADSTS500117 で弾かれる |
http://clientxyz.localhost:3000 | 不可 | 同上 |
https://app.localhost:3000 | 許可 | HTTPS ならサブドメインもOK |
つまり、HTTP を使えるのは ホスト名がちょうど localhost の場合のみであり、サブドメインが付くと通常の外部ドメインと同じ扱いになります。そのため http://app.localhost:3000 は、「安全ではない HTTP の外部リダイレクトURI」と見なされ、AADSTS500117 で拒否されるわけです。
なぜ HTTP の localhost サブドメインが禁止されているのか
Azure AD が http://localhost だけを特別扱いする背景には、次のようなセキュリティ上の理由があります。
- ネイティブアプリ向けの「例外」として設計されている
- モバイル / デスクトップアプリからブラウザを開き、最終的にローカルのループバックサーバーに戻すパターンを許容するため。
- HTTP でのリダイレクトは原則禁止
- ネットワーク上でトークンが盗聴されるリスクがあるため、実運用では HTTPS が必須。
- ホスト名を厳密に制限することでフィッシングを防止
- 例えば
http://login.microsoftonline.com.evil.example.comのような紛らわしいドメインを防ぐ思想と同じ。 *.localhostを広く HTTP 許可すると、誤設定や意図しないホスト名が混入した場合の影響が読みにくくなる。
- 例えば
結果として、「HTTP を許すのは本当に最小限の http://localhost のみ」という仕様になっています。サブドメインがついた瞬間に、「それはもう普通の外部ドメインなので HTTPS で来てください」という扱いになるイメージです。
RFC 6761 と localhost サブドメインの話
DNS に関する RFC 6761 では、localhost. ドメインは特別な名前として定義されており、そのサブドメインもループバックとして扱うことが推奨されています。つまり理屈の上では、次のドメインも「ローカルループバック扱い」にしてよさそうに見えます。
app.localhostclientxyz.localhost
しかし Azure AD の実装上は、RFC の「名前解決上の推奨」と、「ID プロバイダとしてどこまで例外を許すか」は別問題として扱われています。
- 名前解決(DNS / ブラウザ)レベルでは、
*.localhostを 127.0.0.1 に解決するのは妥当。 - OAuth / OIDC のセキュリティポリシーとしては、リダイレクトURIを許可する範囲は極力絞るべき。
このギャップが、開発者からすると「RFC 的には OK なはずなのに、Azure AD 側の制限で HTTP が使えない」というモヤモヤにつながっているわけです。
現時点の仕様として押さえておくポイント
この記事では、次のような仕様で動作している前提で解説します。
- HTTP を許可するのは
http://localhost(ポート指定可)のみ http://app.localhostなど localhost サブドメインは HTTP では不可- サブドメインや任意のホスト名を使いたい場合は、HTTPS が必須
- この制限はセキュリティ要件に基づくもので、緩和のアナウンスは(少なくとも一般公開情報としては)確認されていない
そのため、開発者として現実的に取れる方針は次のどれかになります。
- ローカルでも HTTPS を立てて Azure AD の仕様に合わせる
- テスト用の独自ドメインを取得し、そこを HTTPS でローカルに向ける
- ngrok や devtunnels などのパブリックトンネルで HTTPS エンドポイントを用意する
- 要件によっては、開発中だけ GitHub / Google 等の別 IdP を使う
実運用で使えるワークアラウンドの比較
代表的な回避策を、メリット・デメリットとともに一覧表にしました。
| 方法 | 概要 | メリット | デメリット |
|---|---|---|---|
| ローカルで HTTPS を立てる | 自己署名 or 開発用 CA(mkcert 等)で証明書を発行し、https://app.localhost:3000 に対応させる | Azure AD の仕様に完全準拠 本番とほぼ同じ構成でテスト可能 | 証明書の作成・インポートが面倒 全開発マシンで信頼済みルートを登録する必要 |
| テスト用の独自ドメインを取得 | 例: dev.myapp.test を 127.0.0.1 に向け、HTTPS 化して利用 | URI をそのままクラウドのステージング環境にも流用しやすい ドメイン戦略を本番と近づけられる | ドメイン購入や DNS 設定が必要 CI/CD 用の別テスト環境とサブドメインが衝突する可能性 |
| パブリックトンネル(ngrok / devtunnels)を利用 | ローカルの https://localhost:3000 をインターネット経由の HTTPS URL に公開 | 証明書を自前で用意しなくてもよい モバイル実機や外部メンバーからの動作確認が簡単 | 外部経由のため遅延や通信コストが増える 無料枠の制限・ URL が変わりやすいなどの運用負荷 |
| 他の OAuth プロバイダーで開発 | GitHub / Google 等、http://app.localhost を許可する IdP を利用 | ローカル開発が非常に手軽 既存のサンプル・ライブラリが豊富 | Azure AD 固有機能(ロール、条件付きアクセス等)は試せない 本番切り替え時に認証コードの差分が増えるリスク |
この中で、長期的に最もおすすめなのは「ローカルでも HTTPS を立てる」パターンです。少々手間でも、一度仕組みさえ作ってしまえば、以降のプロジェクトでも再利用できます。
ローカルで HTTPS を立てる具体的な手順例
ここでは代表的なツールである mkcert を例に、https://app.localhost:3000 を立てる流れをイメージできるように整理します。
mkcert のざっくり手順
- 各開発マシンに mkcert をインストールする。
- 開発マシンにローカル CA(開発用ルート証明書)をインストールする。
app.localhost向けのサーバー証明書を発行する。- フロントエンド開発サーバー(例: Vite / webpack-dev-server / ASP.NET Core)で、その証明書を読み込むよう設定する。
コマンド例イメージ(実行環境に合わせて調整してください)。
mkcert -install
mkcert app.localhost
これで app.localhost.pem と app.localhost-key.pem のようなファイルが生成されます。あとは開発サーバーに設定するだけです。
フロントエンド(例: Vite)の設定例イメージ
// vite.config.ts(イメージ)
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import fs from 'fs';
export default defineConfig({
plugins: [react()],
server: {
https: {
key: fs.readFileSync('./cert/app.localhost-key.pem'),
cert: fs.readFileSync('./cert/app.localhost.pem'),
},
host: 'app.localhost',
port: 3000,
},
});
ブラウザ側に mkcert のルート証明書がインポートされていれば、https://app.localhost:3000 で警告なしにアクセスできるようになります。この URL をそのまま Azure AD のリダイレクトURIとして登録すれば、http://app.localhost を使っていた頃とほぼ同じ感覚で開発できます。
テスト用ドメインを取得してローカルに向けるパターン
より本番に近い構成を目指す場合、テスト専用ドメインを取得するのも有力です。
- 例:
dev.myapp.exampleを本番と別途用意 - ローカル環境では hosts ファイルや DNS で 127.0.0.1 に向ける
- ステージング環境では実際のテスト用サーバーに向ける
このアプローチでは、ローカルでもステージングでも https://app.dev.myapp.example のような同一URIで動作させやすくなります。ただし以下の点に注意が必要です。
- ドメイン名の管理・更新コスト(更新忘れによるトラブルなど)
- CI / E2E テストで使うサブドメインとの設計調整
- 証明書をどこまで自動化できるか(Let’s Encrypt / ACME クライアント等)
チームやサービス規模が大きく、ステージング環境も充実させたいケースでは、ローカル専用の *.localhost だけで完結させるよりも、長期的にメリットが出やすい構成と言えるでしょう。
パブリックトンネル(ngrok / devtunnels)の活用
「とりあえず動作確認したい」「モバイル実機から Azure AD 認証を試したい」というときには、パブリックトンネルが手早い選択肢になります。
- ngrok: ローカルポートを外部の
https://xxxx.ngrok.ioに公開 - Dev Tunnels(Visual Studio / VS Code 統合): Microsoft 製のトンネル機能
これらを使えば、ローカルの HTTP / HTTPS サーバーを、Azure AD からアクセス可能な HTTPS URL に変換できます。この URL をリダイレクトURIとして登録すれば、
- 証明書の生成・インポート
- hosts や DNS の設定
などの手間をかけずに、即座に Azure AD 認証を試せます。
一方で、以下の点には注意してください。
- インターネット経由になるため、ローカル専用より遅延や不安定さが増える
- 無料プランだと URL が都度変わったり、同時接続数に制限がある
- 機密データを扱う場合は、そもそも外部トンネルを使うべきかを慎重に検討する必要がある
そのため、一時的な検証やデモ用途として割り切って使い、本格的な開発フェーズでは「ローカルで HTTPS」路線に寄せていく、という使い分けが現実的です。
他 IdP での開発と Azure AD 本番の二段構え
もう一つの考え方として、
- ローカル開発中は GitHub / Google などの IdP で気軽に開発
- ステージング以降は Azure AD(Entra ID)に切り替えて最終確認
という二段構えもあります。特に
- シンプルな SPA + API の構成
- Azure AD 依存機能(グループ、ロール、条件付きアクセスなど)が薄い
といったプロジェクトでは、「とにかく開発効率優先」でこのような構成にするチームも存在します。
ただし、最終的に Azure AD 本番に乗せる以上、
- トークン形式(スコープ、クレーム)の違い
- ログイン画面の挙動や多要素認証(MFA)の有無
など、本番とのギャップがどうしても生じます。要件が Azure AD に強く依存する場合は、この方法はあくまで「参考程度」にとどめるのがおすすめです。
Azure AD ポータルでの設定例
実際に Azure ポータルで設定するときの流れも整理しておきます。
SPA(シングルページアプリ)の例
- Azure ポータル > Microsoft Entra ID > アプリ登録 を開く。
- 対象のアプリを開き、「認証」メニューに移動する。
- 「プラットフォームの追加」から 「シングルページ アプリケーション」 を選択する。
- リダイレクトURIとして、例えば
https://app.localhost:3000を追加する。 - 必要に応じて
https://clientxyz.localhost:3000なども追加する。
MSAL.js の設定イメージは次のようになります。
// msalConfig.ts(イメージ)
import { Configuration } from '@azure/msal-browser';
export const msalConfig: Configuration = {
auth: {
clientId: '<アプリケーション (クライアント) ID>',
authority: 'https://login.microsoftonline.com/<テナントID>',
redirectUri: 'https://app.localhost:3000',
postLogoutRedirectUri: 'https://app.localhost:3000',
},
};
ここで redirectUri が HTTP(http://app.localhost:3000)になっていると、AADSTS500117 が発生します。必ず HTTPS に揃えるようにしてください。
AADSTS500117 エラーが出たときのチェックリスト
AADSTS500117: The reply uri specified in the request isn't using a secure scheme. が出た場合、次のポイントを順に確認すると原因を特定しやすくなります。
- HTTP になっていないか
http://localhost:3000以外の HTTP URI はすべて NG。http://app.localhost:3000やhttp://127.0.0.1:3000は使えない。
- ポート番号が一致しているか
- 実際のリクエストが
https://app.localhost:3001なのに、ポータルにはhttps://app.localhost:3000しか登録していない等。
- 実際のリクエストが
- 余計なパスやクエリが入っていないか
- ポータル側に
https://app.localhost:3000を登録したのに、リクエストではhttps://app.localhost:3000/callbackになっている、など。
- ポータル側に
- MSAL 等のライブラリ設定が複数箇所で上書きされていないか
- 環境変数や設定ファイルをまたぐうちに、
redirectUriの値が意図せず HTTP に書き換わっているケースもあります。
- 環境変数や設定ファイルをまたぐうちに、
ログ出力やブラウザのネットワークタブで、実際に投げられている redirect_uri パラメータの値を確認し、ポータルに登録したものと 1 文字も違いがないかチェックするのがトラブルシューティングの近道です。
今後の見通しと仕様変更への備え
コミュニティでは以前から、
*.localhostも RFC 6761 に従ってループバック扱いにしてほしい- 開発用途に限って、HTTP の localhost サブドメインを許可してほしい
といった要望が継続的に上がっています。とはいえ、ID プロバイダーにとってリダイレクトURIの制限はセキュリティに直結するため、慎重な判断が必要です。
実務としては、次のようなスタンスを取るのが安全です。
- 「ローカルでも HTTPS」が今後も推奨され続ける前提で設計する
- 開発テンプレートやスキャフォールドに、mkcert 等を組み込んでおく
- Azure AD や OAuth の仕様変更があっても、自分たちのコード側の変更を最小限に抑えられるようにする
もし将来、http://app.localhost のような形が正式に許可されるような仕様変更があれば、それはそれで選択肢として歓迎できますが、それを前提に設計を引き延ばすのではなく、今ある仕様の中で安定した開発基盤を作っておくことが重要です。
まとめ:localhost サブドメイン問題をどう捉えるか
最後に、この記事の要点を整理します。
- Azure AD(Microsoft Entra ID)は、HTTP のリダイレクトURIとして
http://localhostだけを許可している。 http://app.localhostやhttp://clientxyz.localhostといった localhost サブドメインは HTTP では使用できないため、AADSTS500117エラーになる。- サブドメインを使いたい場合は HTTPS が必須であり、ローカルでも証明書を用意して
https://app.localhost:3000のように構成するのが王道。 - ワークアラウンドとしては、
- ローカルで HTTPS を立てる(最もおすすめ)
- テスト用の独自ドメインを取得してローカルに向ける
- ngrok / devtunnels などで一時的に公開する
- 場面によっては他 IdP で開発し、Azure AD で最終確認する
- 将来仕様が緩和される可能性はゼロではないものの、「ローカルも HTTPS」を前提とした開発フローを整えておくのが長期的に安全。
「localhost サブドメインを HTTP で使えない」という制約は一見不便ですが、視点を変えると「開発環境から本番環境まで一貫して HTTPS ベースで設計できる」というメリットもあります。チームの標準テンプレートやドキュメントとして、ここで紹介した HTTPS セットアップ手順やワークアラウンドをまとめておくと、今後新しいプロジェクトを立ち上げるときの大きな助けになるはずです。

コメント