ASP.NET Core 6 の最小ホスティングへ移行した途端、appsettings.json に書いた "Urls" が効かず、launchSettings.json のランダムポートで起動してしまう――開発現場で非常に起きやすい落とし穴です。本稿では「なぜ無視されるのか」「どう直すのが最適か」を、.NET 6/7/8/9 にわたる動作差・優先順位・具体レシピ・本番運用の勘所までまとめて解説します。コピペできる設定例・実行例・トラブルシュートも多数掲載します。
「Urls が無視される」現象の正体
結論:.NET 6 以降の最小ホスティング(WebApplication.CreateBuilder)では、ホスト構成(Host Configuration)とアプリ構成(App Configuration)が明確に分かれ、appsettings.json はアプリ構成側へ読み込まれます。一方、サーバーの待ち受けアドレス(urls)はホスト構成のキーです。したがって appsettings.json の "Urls" は既定では消費されず、launchSettings.json から供給される ASPNETCORE_URLS(= 環境変数)や --urls(= コマンドライン)の方が優先して適用されます。
特に dotnet run は launchSettings.json の applicationUrl を検出すると、内部的に ASPNETCORE_URLS を設定して起動します。この時点で appsettings.json の "Urls" より強力なソースが存在するため、「Urls が無視された」ように見えるわけです。
まず押さえるべき「推奨アプローチ」早見表
| 目的 | 推奨アプローチ | 補足 |
|---|---|---|
| ポート/URLをJSONだけで固定したい | Kestrel:Endpoints セクションを使う | 最優先で適用され、launchSettings.jsonより勝つ |
| 環境ごとに実行時に切替たい | 環境変数 ASPNETCORE_URLS(または CLI の --urls) | dotnet run --urls "http://*:5000;https://*:5001" |
| コードで制御したい | builder.WebHost.UseUrls() | 複数URL指定可。必要なら ConfigureKestrel と併用 |
Kestrel:Endpoints で確実に固定する
最小ホストでポートを確実に固定したい場合、Kestrel:Endpoints を使うのが最も堅牢です。これはホスト構成よりも優先されるため、launchSettings.json のランダムポートや ASPNETCORE_URLS を上書きします。
JSON 例(HTTP/HTTPS + 既定プロトコル)
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://0.0.0.0:5000" },
"Https": { "Url": "https://0.0.0.0:5001" }
},
"EndpointDefaults": {
"Protocols": "Http1AndHttp2"
}
}
}
ポイント:*(ワイルドカード)も使えますが、本番ではセキュリティの観点から 0.0.0.0 か特定のIPを推奨します。
証明書を使う(PFXファイル指定)
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://0.0.0.0:5001",
"Certificate": {
"Path": "certs/site.pfx",
"Password": "P@ssw0rd!"
}
}
}
}
}
開発時は dotnet dev-certs https --trust による開発用証明書で十分ですが、本番は必ず実運用証明書を指定してください。
環境変数・CLIで切替える(柔軟・手早い)
環境変数 ASPNETCORE_URLS(推奨)
OS問わず使える最短ルートです。CI/CD・コンテナ・PaaS など「コード/JSONに触れたくない」状況で威力を発揮します。
# Windows PowerShell
$env:ASPNETCORE_URLS = "http://*:5000;https://*:5001"
dotnet run
# Linux / macOS
export ASPNETCORE_URLS="http://*:5000;https://*:5001"
dotnet run
コマンドライン --urls(一時的な試験に)
dotnet run --urls "http://*:5000;https://*:5001"
ASPNETCORE_URLS と同じ効果です。使い分けはお好みで。
コードで指定:UseUrls と ConfigureKestrel
var builder = WebApplication.CreateBuilder(args);
// シンプルにURLだけ固定
builder.WebHost.UseUrls("[http://0.0.0.0:5000](http://0.0.0.0:5000)", "[https://0.0.0.0:5001](https://0.0.0.0:5001)");
// さらにKestrelを詳細制御したい場合(UseUrlsよりこちらが強い)
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5000);
options.ListenAnyIP(5001, listen => listen.UseHttps()); // 証明書は既定/構成で解決
});
var app = builder.Build();
app.MapGet("/", () => "OK");
app.Run();
注意:同時に設定した場合は ConfigureKestrel(および Kestrel:Endpoints)が勝ちます。重複設定は混乱のもと。どれか一つに寄せるのが安全です。
優先順位を“公式挙動”ベースで理解する
アドレス解決の優先順位は下表のとおりです。上にあるほど強く、後ろの層を上書きします。
| 優先 | ソース | 例 | 補足 |
|---|---|---|---|
| 最優先 | Kestrel エンドポイント | Kestrel:Endpoints / ConfigureKestrel() | 他の Urls 指定より常に強い |
| 高 | コードの UseUrls() | builder.WebHost.UseUrls("http://*:5000") | 環境変数や CLI より原則強い |
| 中 | 環境変数 / CLI | ASPNETCORE_URLS / --urls | launchSettings.json の applicationUrl もここ |
| 低 | appsettings.json の "Urls" | { "Urls": "http://*:5000" } | .NET 6 以降の最小ホストでは既定で無視 |
なぜ .NET 5 では効いていたのか
.NET 5 の典型テンプレートは CreateHostBuilder(Generic Host + 旧式スタイル)を採用しており、Urls をアプリ構成からも拾う形で動くケースがありました。.NET 6 以降の最小ホストは、起動の早い段階で「ホスト構成のみ」を見てサーバーを初期化します。ここに appsettings.json の "Urls" は含まれないため、結果として「効かない」状態になります。
launchSettings.json による“ランダムポート”のからくり
launchSettings.json の各プロファイルにある applicationUrl は、実行時に ASPNETCORE_URLS としてプロセスへ注入されます。Visual Studio 生成のテンプレートはここに https://localhost:xxxxx のような動的ポートを設定するため、dotnet run や IDE 実行時にその値で立ち上がります。Kestrel:Endpoints を使えばこれを上から抑え込めるので、開発と本番でポートがぶれない運用が可能です。
launchSettings.json 例
{
"profiles": {
"MyApp": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
.NET 7 以降の既定挙動(HTTPS 自動バインドの削除)
.NET 7 以降では、既定の https://localhost:5001 自動バインドが削除されました。つまり、HTTPS を使いたい場合は Kestrel:Endpoints、ASPNETCORE_URLS、--urls、あるいは UseUrls() で自分で明示する必要があります。既存プロジェクトをアップグレードした際、「HTTP は生きているが HTTPS で応答しない」という症状はこの変更が原因であることが多いです。
運用シナリオ別レシピ集
Linux systemd サービス
[Unit]
Description=My ASP.NET Core App
[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet MyApp.dll
Restart=always
RestartSec=5
SyslogIdentifier=myapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=[http://0.0.0.0:5000](http://0.0.0.0:5000)
[Install]
WantedBy=multi-user.target
Docker(単体コンテナ)
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY ./publish .
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000
ENTRYPOINT ["dotnet","MyApp.dll"]
Docker Compose
services:
web:
build: .
ports:
- "5000:5000"
environment:
- ASPNETCORE_URLS=http://+:5000
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 2
selector:
matchLabels: { app: myapp }
template:
metadata: { labels: { app: myapp } }
spec:
containers:
- name: web
image: myrepo/myapp:latest
env:
- name: ASPNETCORE_URLS
value: "http://+:5000"
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
selector: { app: myapp }
ports:
- port: 80
targetPort: 5000
type: ClusterIP
逆プロキシ(Nginx / Apache)と組み合わせる
外向き TLS はリバースプロキシに任せ、Kestrel はローカル待受に限定するのが定番です。
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://127.0.0.1:5000" }
}
}
}
Tip:外側のプロキシで X-Forwarded-For/X-Forwarded-Proto を正しく付与/信頼する設定(UseForwardedHeaders)も忘れずに。
IIS / IIS Express のとき
IIS(in-process)では HTTP.sys がフロントに立つため、Kestrel が直接ポートを公開しません。Urls や Kestrel:Endpoints をいくら弄ってもブラウザ側の接続ポートは IIS のバインドに依存します。IIS 管理側でサイトのバインドを設定してください。
環境ごとの appsettings 戦略
JSONで固定運用するなら、appsettings.{Environment}.json を切り分けるのが王道です。
// appsettings.Development.json
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://localhost:5000" },
"Https": { "Url": "https://localhost:5001" }
}
}
}
// appsettings.Production.json
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://10.0.0.10:80" },
"Https": { "Url": "https://10.0.0.10:443" }
}
},
"AllowedHosts": "example.com;api.example.com"
}
AllowedHosts はホストヘッダーの検査(Host Filtering)。リバースプロキシ配下やワイルドカード待受時の安全策として設定を推奨します。
セキュリティ観点の注意点
- ワイルドカード待受(
*や0.0.0.0)は本番では最小限に。必要ならファイアウォール/NACLで外部公開ポートを絞ること。 - HTTPS 終端を Kestrel 側で行う場合は、鍵保護・自動更新(ACME 等)・ピン留め監視を設計へ。
- プロキシ配下は
UseForwardedHeadersを設定し、KnownProxies/KnownNetworksを絞り込む。 - HTTP/2 を使うなら、ALPN/証明書の互換性も併せて確認。
よくある誤解と対処
| 誤解 | 正しい理解 | 対処 |
|---|---|---|
「Urls は非推奨になった」 | 非推奨ではない。環境変数/CLI 由来では今も有効 | ASPNETCORE_URLS か --urls を使う |
「appsettings.json に書けば動くはず」 | .NET 6+ 最小ホストでは既定では読まれない | Kestrel:Endpoints へ移行する |
「launchSettings.json を消せばOK」 | IDE実行は便利だが、本番に影響はない | 本番は Kestrel:Endpoints または ASPNETCORE_URLS を信頼 |
「UseUrls と Kestrel を両方書けば安心」 | 二重指定は衝突源。Kestrel 設定が勝つ | どちらか一方に寄せる |
チェックリスト(原因切り分けの快速ルート)
- 実行コマンドを確認:
dotnet runか IDE か、サービス起動か。 launchSettings.json:applicationUrlがランダムポートになっていないか。- 環境変数:
ASPNETCORE_URLSが残っていないか(CI/CD での上書きを含む)。 - Kestrel 設定:
Kestrel:Endpointsは正しく意図したポートか。HTTP/HTTPS の両方を定義したか。 - 証明書:HTTPS であればパス・パスワード・アクセス権限を確認。
- 競合プロセス:対象ポートが既に使用中でないか(Windows:
netstat -ano/ Linux:ss -lntp)。 - プロキシ配下:IIS/Nginx/Apache などの外側のバインドが意図どおりか。
実務で役立つスニペット集
「開発はランダム・本番は固定」を両立する
// appsettings.Production.json
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://0.0.0.0:8080" },
"Https": { "Url": "https://0.0.0.0:8443" }
}
}
}
開発時は launchSettings.json のランダムポートをそのまま使い、本番は JSON で固定。同じビルド成果物でも環境だけで挙動を切り替えられます。
環境変数で一時避難
# ひとまず衝突回避して起動確認
export ASPNETCORE_URLS=http://+:5050
dotnet run
HTTP/2 を常にオン(自己署名でも)
{
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http1AndHttp2"
}
}
}
トラブルシュート:代表的な症状と対処
| 症状 | 原因 | 解決策 |
|---|---|---|
| 指定ポートで起動しない | launchSettings.json の applicationUrl が上書き | Kestrel:Endpoints へ移行 or いったん削除/修正 |
| HTTPS だけ応答しない | .NET 7+ の既定変更/証明書不備 | HTTPS エンドポイントを明示し、証明書設定を点検 |
本番で * 待受が危険 | ワイルドカードバインドによる過剰露出 | 特定IPに限定+FW/NACL で外部公開を絞る |
| ポートが使用中 | 他プロセスが占有 | プロセス特定(netstat/ss)→ 停止 or ポート変更 |
IIS で Urls が効かない | in-process では IIS バインドが支配 | IIS 側のサイトバインドを設定 |
短い結論
- .NET 6 以降、
appsettings.jsonの"Urls"だけではポートは変わらない。 - 固定したいなら
Kestrel:Endpoints。状況に応じてASPNETCORE_URLS/--urls/UseUrls()を使い分け。 - .NET 7+ ではHTTPS の自動バインドが無いので、HTTPS も自分で構成する。
実務フローのおすすめ(テンプレ)
- まず
Kestrel:Endpointsをappsettings.{Environment}.jsonに定義。 - 開発の利便性は
launchSettings.jsonで確保(気になるならapplicationUrlを消す)。 - 本番は環境変数で上書き可能にしておく(
ASPNETCORE_URLS)。 - IIS/プロキシ/コンテナなど外部の“入口”がある場合、そちらのバインド/転送ヘッダーを最優先に点検。
サンプル一式(最小アプリ)
以下をそのまま使えば、HTTP :5000 / HTTPS :5001 で起動します。launchSettings.json のランダムポートに悩まされません。
Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello");
app.Run();
appsettings.json
{
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://0.0.0.0:5000" },
"Https": { "Url": "https://0.0.0.0:5001" }
}
}
}
最後に:選択の指針
「設定は1か所に寄せる」――これが、.NET 6 以降で Urls の混乱を避ける最大のコツです。JSONで固定するなら Kestrel:Endpoints に一本化。運用の変化へ強くしたいなら ASPNETCORE_URLS と CLI を主導に。コードで自動判定したいなら UseUrls や ConfigureKestrel を条件分岐。いずれの手段でも、優先順位とプロセス外の入口(IIS/プロキシ/コンテナ)を理解しておけば、もう「Urls が無視される」罠にはハマりません。

コメント