Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
Asp.Net Core

ASP.NET Core: Fix a File Upload That Times Out on Slow Connections Only Because Kestrel’s Minimum Data Rate Feature Kicked In

- 04.10.26 - ErcanOPAK

⚙️ The Upload That Failed Only on the Slowest Connections

Kestrel enforces a minimum data rate on both reading a request and writing a response, dropping the connection if a client sends or receives data more slowly than the configured floor for more than a short grace period – a safety feature meant to free up server resources from a client that has effectively stalled. A large file upload from a genuinely slow or congested connection can trip that same floor well before the upload would have otherwise finished, aborting a transfer that a generous request body size limit and a generous request timeout both would have allowed to complete. The failure looks exactly like a generic connection reset to the client, with nothing in the client-visible error pointing at the server setting that actually caused it.

🔎 The Problem

// A generous size limit and request timeout both suggest this
// upload should be allowed to take its time:
builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 500_000_000;
});
builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.Limits.MaxRequestBodySize = 500_000_000;
});

// But Kestrel's default minimum data rate still applies underneath
// both of those settings - a client sustaining less than roughly
// 240 bytes/second for more than a short grace period gets its
// connection aborted mid-upload, regardless of the body size limit
// or how generous any other timeout was configured to be:
//   MinRequestBodyDataRate: 240 bytes/sec, 5 second grace period
// (the actual Kestrel default, unless explicitly overridden)

✅ Fix: Set the Minimum Data Rate to Match What Slow Uploads Actually Need

  • Override MinRequestBodyDataRate explicitly for the specific upload endpoint, lowering the bytes-per-second floor or disabling the check entirely for that route rather than for the whole application, once you know real clients upload from genuinely slow connections.
  • Reproduce the failure by actually throttling a test upload’s bandwidth to a realistic worst case, instead of relying on a fast local network connection where the minimum data rate floor is never remotely close to being tripped.
  • Log the specific exception Kestrel raises when it aborts a connection for falling below the minimum rate, since it is distinct from a generic timeout and immediately points at the correct setting instead of leaving the cause to be guessed at from a plain connection reset.

⚠️ Why This Is Easy to Miss

  • Every other timeout and size limit involved in the upload path can be configured generously and still not matter, because the minimum data rate check operates independently of all of them and enforces its own floor regardless of what the rest of the configuration allows.
  • The failure only reproduces on a connection slow enough to fall under the floor for long enough to trigger it, so testing on a typical office or home connection almost never reveals the problem – it surfaces only once real users on weak mobile or rural connections try the same upload.

A generous size limit tells Kestrel how much data it will accept – the minimum data rate tells it how slowly it will accept that data, and the two settings answer completely different questions.

— Backend Architect

Related posts:

ASP.NET Core Requests Hang Under Load

.NET Core: Use Global Using Directives to Avoid Repeating Imports

.NET Core: Implement Built-in Health Checks for Microservices

Post Views: 0

Post navigation

JavaScript: Fix an Array Destructuring Default Value That Silently Never Applies Because null Was Passed Instead of Undefined
Visual Studio: Fix a Test Project That Builds Fine Alone but Fails to Discover Any Tests After a NuGet Restore

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (947)
  • How to add default value for Entity Framework migrations for DateTime and Bool (938)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (624)
  • Add Constraint to SQL Table to ensure email contains @ (590)
  • Average of all values in a column that are not zero in SQL (553)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (526)
  • Find numbers with more than two decimal places in SQL (468)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps
  • Photoshop: Fix a Color Profile Mismatch That Makes Printed Output Look Nothing Like What You Saw On Screen
  • WordPress: Fix Search Results That Return Pages From a Theme You Deactivated Months Ago
  • Visual Studio: Fix a Test Project That Builds Fine Alone but Fails to Discover Any Tests After a NuGet Restore
  • ASP.NET Core: Fix a File Upload That Times Out on Slow Connections Only Because Kestrel’s Minimum Data Rate Feature Kicked In
  • JavaScript: Fix an Array Destructuring Default Value That Silently Never Applies Because null Was Passed Instead of Undefined

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (947)
  • How to add default value for Entity Framework migrations for DateTime and Bool (938)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com