We still get tickets where a .NET 10 app “allows 100 MB uploads” in code and fails at ~28–30 MB in the browser. The status is not 413 from Kestrel. It is IIS 404.13 — content length too large — and the request never reaches your middleware.
On Windows hosting the stack is two gates: IIS requestFiltering first, then ANCM into the ASP.NET Core app (in-process or out-of-process). App-only limits look correct in Program.cs and still lose to the default maxAllowedContentLength of 30000000 bytes.
#One incident, two knobs
A reseller site raised Kestrel’s MaxRequestBodySize and FormOptions MultipartBodyLengthLimit, republished, and retested. Failed. Failed-request tracing pointed at RequestFilteringModule, not the managed pipeline. web.config had no requestLimits override, so IIS kept the 30 MB ceiling while the app advertised 100 MB.
Symptoms to memorize: 404.13 in the IIS log sc-substatus, often with a generic client error page; nothing useful in ASP.NET Core stdout unless the body actually enters the process. If you only watch app logs, you will “fix” the wrong layer.
<system.webServer>
<security>
<requestFiltering>
<!-- bytes; example = 100 MB -->
<requestLimits maxAllowedContentLength="104857600" />
</requestFiltering>
</security>
<aspNetCore processPath="dotnet" arguments=".\MyApp.dll" hostingModel="inprocess" />
</system.webServer>
Match that number in the app: Kestrel ServerOptions.Limits.MaxRequestBodySize, FormOptions.MultipartBodyLengthLimit, and any endpoint-specific RequestSizeLimit attributes. Use the same byte value everywhere. Mismatched limits waste time — IIS rejects early, or Kestrel rejects after IIS already accepted the body.
#What we refuse to do
Do not “fix” upload failures by disabling requestFiltering or setting absurd multi-gigabyte caps site-wide. That turns a size bug into an easy memory and disk exhaustion path on a shared app pool. Cap to what the feature needs, keep antiviruses and temp-folder cleanup in mind, and document the paired web.config + Kestrel values in the publish notes.
If large bodies are rare, prefer direct-to-object or chunked processing over buffering the full payload in memory. Limits protect the worker process; they are not a substitute for streaming I/O. After you change requestLimits, recycle the app pool once and confirm with a known-size POST so you are not debugging a cached web.config copy.
Comments
No comments yet