ASP.NET Core 6以降でappsettings.jsonのUrlsが無視される理由と対処:Kestrel EndpointsとASPNETCORE_URLSで確実にポート固定する方法

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 より原則強い
中環境変数 / CLIASPNETCORE_URLS / --urlslaunchSettings.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 設定が勝つどちらか一方に寄せる

チェックリスト(原因切り分けの快速ルート)

  1. 実行コマンドを確認:dotnet run か IDE か、サービス起動か。
  2. launchSettings.json:applicationUrl がランダムポートになっていないか。
  3. 環境変数:ASPNETCORE_URLS が残っていないか(CI/CD での上書きを含む)。
  4. Kestrel 設定:Kestrel:Endpoints は正しく意図したポートか。HTTP/HTTPS の両方を定義したか。
  5. 証明書:HTTPS であればパス・パスワード・アクセス権限を確認。
  6. 競合プロセス:対象ポートが既に使用中でないか(Windows: netstat -ano / Linux: ss -lntp)。
  7. プロキシ配下: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 も自分で構成する。

実務フローのおすすめ(テンプレ)

  1. まず Kestrel:Endpoints を appsettings.{Environment}.json に定義。
  2. 開発の利便性は launchSettings.json で確保(気になるなら applicationUrl を消す)。
  3. 本番は環境変数で上書き可能にしておく(ASPNETCORE_URLS)。
  4. 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 が無視される」罠にはハマりません。

この記事を書いた人

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

コメント

コメントする

目次