Start Debugging

修正: ASP.NET Core エンドポイントへのファイルアップロードで発生する「413 Request Entity Too Large」

Kestrel はデフォルトでリクエストボディを 30,000,000 バイトに制限し、超過すると 413 を返します。MaxRequestBodySize をグローバル、エンドポイント単位、または IIS の背後では web.config で引き上げましょう。

アップロードが 413 Request Entity Too Large を返すのは、Kestrel がすべてのリクエストボディをデフォルトで 30,000,000 バイト (約 28.6 MiB) に制限しており、ファイルがそれより大きいためです。上限は実際に効く場所で引き上げてください。Kestrel の MaxRequestBodySize をグローバルに設定し、コントローラーのアクションには [RequestSizeLimit] または [DisableRequestSizeLimit] を付与し、フォーム投稿では FormOptions.MultipartBodyLengthLimit を引き上げ、IIS の背後では web.configmaxAllowedContentLength を引き上げます。ASP.NET Core 11 (.NET 11、C# 14) で検証済みです。上限とデフォルト値は .NET 8 から .NET 11 まで同一です。

このエラーが起きる状況

現れ方は 2 通りあります。1 つ目はクライアントが受け取る HTTP レスポンスです。

HTTP/1.1 413 Request Entity Too Large
Content-Length: 0
Connection: close
Date: Fri, 17 Jul 2026 10:04:11 GMT

2 つ目はサーバー側で Kestrel が書き出すログ行で、どの上限が発火したかを正確に教えてくれるのはこちらです。

Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException:
Request body too large. The max request body size is 30000000 bytes.

この 30000000 が手がかりです。何も変更していなければ、メッセージ内の数値はフレームワークのデフォルトであり、修正はその上限を所有するレイヤーで上限を引き上げるか撤廃することです。数値が別の値なら、誰かがすでに上限を設定していて、それに引っかかっています。いずれにしても原因は同じで、リクエストボディが設定された最大値を超え、ハンドラーが読み終える前にサーバーが接続を拒否したのです。

なぜこれが起きるのか

アップロードは 4 つの異なる上限のいずれかでブロックされ得ますが、それらは別々の場所に存在します。正しく修正するには、どれがあなたの 413 を生んだのかを知る必要があります。

Kestrel に直接なら動くのにプロキシや IIS の背後で失敗する場合、変更すべき上限はあなたの C# コードにはありません。まず最も外側のレイヤーを確認してください。

最小限の再現

デフォルトの 413 を再現する最小のエンドポイントです。カスタムの上限はなく、フレームワークのデフォルトのみです。

// .NET 11, ASP.NET Core 11, C# 14 -- Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/upload", async (HttpRequest request) =>
{
    // Reading the body is what trips the limit
    using var reader = new StreamReader(request.Body);
    var content = await reader.ReadToEndAsync();
    return Results.Ok(new { bytes = content.Length });
});

app.Run();

30,000,000 バイトより大きいものを POST すると、読み取りがスローします。

# .NET 11 -- generate a 40 MB file and POST it
head -c 40000000 /dev/urandom > big.bin
curl -i -X POST http://localhost:5000/upload \
  -H "Content-Type: application/octet-stream" \
  --data-binary @big.bin
# -> HTTP/1.1 413 Request Entity Too Large

この 40 MB のペイロードは 128 MiB のマルチパート上限はクリアします (そもそもマルチパートではありません) が、30 MB の Kestrel ボディ上限を突破するため、400 ではなく 413 が返ります。

上限を効く場所で引き上げる

413 がどこから来ているかに応じて、次を順に確認してください。ほとんどの自己ホスト型 Kestrel アプリでは最初の 1 つだけで済みます。

1. Kestrel のグローバル上限を引き上げるか撤廃する

KestrelServerOptions.Limits.MaxRequestBodySize を起動時に設定します。特定のバイト数を設定するか、上限を完全に撤廃するには null を設定します。

// .NET 11, ASP.NET Core 11, C# 14 -- Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // 200 MB ceiling for the whole app
    options.Limits.MaxRequestBodySize = 200 * 1024 * 1024;
});

var app = builder.Build();

null よりも実際の上限値を優先してください。ボディサイズを無制限にするのはサービス妨害 (DoS) の攻撃経路です。1 つのクライアントがギガバイト単位をプロセスにストリーミングし、メモリやディスクを枯渇させ得ます。ガードを無効化するのではなく、想定される正当なアップロードの最大値を選び、余裕を持たせてください。

2. コントローラーに属性でエンドポイント単位の上限を設定する

大きなアップロードを受け付けるエンドポイントが 1 つだけなら、アプリ全体のグローバル上限を引き上げないでください。コントローラーのアクションや Razor Page では、[RequestSizeLimit(bytes)] で特定の上限を設定するか、[DisableRequestSizeLimit] でそのアクションだけ上限を撤廃します。どちらの属性も IRequestSizeLimitMetadata を実装しており、MVC パイプラインがリクエストごとにこれを読み取ります。

// .NET 11, ASP.NET Core 11, C# 14
[ApiController]
[Route("api/[controller]")]
public sealed class FilesController : ControllerBase
{
    [HttpPost("upload")]
    [RequestSizeLimit(200 * 1024 * 1024)]   // 200 MB, this action only
    public async Task<IResult> Upload(IFormFile file)
    {
        await using var stream = System.IO.File.Create(
            Path.Combine("uploads", file.FileName));
        await file.CopyToAsync(stream);
        return Results.Ok(new { file.FileName, file.Length });
    }
}

アクションが任意に大きいボディを受け付ける必要があり、別のガード (ストリーミングやクォータチェック) が用意されている場合は、[RequestSizeLimit(...)][DisableRequestSizeLimit] に置き換えてください。

3. minimal API でリクエストごとに上限を設定する

minimal API はコントローラーのようには [RequestSizeLimit] を尊重しません。これを読み取る MVC リソースフィルターがパイプラインに存在しないためです。ボディが読み取られる前に、IHttpMaxRequestBodySizeFeature を通じて命令的に上限を設定してください。

// .NET 11, ASP.NET Core 11, C# 14
app.MapPost("/upload", async (HttpContext context) =>
{
    var sizeFeature = context.Features.Get<IHttpMaxRequestBodySizeFeature>();
    if (sizeFeature is not null && !sizeFeature.IsReadOnly)
    {
        sizeFeature.MaxRequestBodySize = 200 * 1024 * 1024;   // 200 MB
    }

    using var reader = new StreamReader(context.Request.Body);
    var content = await reader.ReadToEndAsync();
    return Results.Ok(new { bytes = content.Length });
});

まず IsReadOnly を確認してください。サーバーがボディの読み取りを開始すると上限はロックされ、代入すると InvalidOperationException がスローされます。これは、この手法が minimal API の IFormFile パラメーターでは機能しない理由でもあります。モデルバインディングがハンドラーの実行前にマルチパートボディを読み取るため、フィーチャーに触れられる頃にはすでに読み取り専用になっているのです (これは dotnet/aspnetcore の issue #50871 の根本原因で、minimal API への大きなマルチパートアップロードが切り詰められたファイルとともに 200 を暗黙に返していました)。minimal API のアップロードでは、HttpContext または HttpRequest をバインドし、上限を引き上げてから request.ReadFormAsync() で自分でフォームを読み取るか、MapGroup に小さなミドルウェアを付けてグループ全体の上限を設定してください。

4. フォーム投稿のためにマルチパートフォーム上限を引き上げる

あなたの 413 が実際にはマルチパートボディ長についての InvalidDataException を伴う 400 なら、発火した上限は Kestrel のものではなく FormOptions.MultipartBodyLengthLimit です。オプションを通じてグローバルに、またはコントローラーのアクション単位で [RequestFormLimits] を使って引き上げます。

// .NET 11, ASP.NET Core 11, C# 14 -- global, in Program.cs
builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 200 * 1024 * 1024;   // 200 MB
});
// .NET 11, ASP.NET Core 11, C# 14 -- per action
[HttpPost("upload")]
[RequestSizeLimit(200 * 1024 * 1024)]
[RequestFormLimits(MultipartBodyLengthLimit = 200 * 1024 * 1024)]
public async Task<IResult> Upload(IFormFile file) { /* ... */ }

大きなアップロードでは通常、両方を引き上げる必要があります。MaxRequestBodySize はリクエスト全体を、MultipartBodyLengthLimit はその中のフォームボディを支配します。両者を同じ値に設定してください。

5. IIS の背後では web.config で maxAllowedContentLength を引き上げる

IIS の背後 (in-process または out-of-process) でホストする場合、IIS のリクエストフィルタリングモジュールが、リクエストが Kestrel に到達する前に maxAllowedContentLength (デフォルト 30,000,000 バイト) を強制します。これも引き上げるまで、Kestrel の上限を引き上げても何も起きません。web.config に追加してください。

<!-- web.config -- value is in bytes; 200 MB here -->
<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="209715200" />
    </requestFiltering>
  </security>
</system.webServer>

in-process ホスティングモジュールには、IISServerOptions を通じてコードで設定できる 2 つ目の別個の上限があります。

// .NET 11, ASP.NET Core 11, C# 14 -- in-process IIS hosting
builder.Services.Configure<IISServerOptions>(options =>
{
    options.MaxRequestBodySize = 200 * 1024 * 1024;   // 200 MB
});

IIS の背後では、web.configmaxAllowedContentLengthIISServerOptions.MaxRequestBodySize、Kestrel の MaxRequestBodySize の 3 つすべてを揃える必要があるかもしれません。リクエストは最も小さいものによって拒否されます。

6. リバースプロキシの上限を引き上げる

アプリの前段にプロキシがある場合、それは独自の上限を持ち、アプリが関与する前に 413 を返します。nginx では、ディレクティブは client_max_body_size です (デフォルト 1 MiB で、これは Kestrel のデフォルトよりはるかに小さく、よくある落とし穴です)。

# nginx.conf -- allow 200 MB uploads through the proxy
client_max_body_size 200M;

YARP、API ゲートウェイ、クラウドロードバランサーでは、そのレイヤーの設定で相当するリクエストサイズの設定を探してください。プロキシがボディを通すまで、アプリ側の上限を引き上げても役に立ちません。

落とし穴とバリエーション

メンタルモデルはこうです。アップロードはサイズのゲートのスタック (プロキシ、IIS、Kestrel のボディサイズ、マルチパートフォームサイズ) を通過し、最初に超過したゲートで拒否されます。あなたの 413 を生んだレイヤーを見つけ、その上限を実際の有界な値に引き上げ、より大きな上限がより大きな負債にならないようストリーミングと組み合わせてください。

関連

出典

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< 戻る