Upload large content with Http.sys, IIS, ASP and ASP.NET

Posted on Updated on

There is a limit on the size of content (like files) one can upload when that content is hosted on an Internet Information Services (IIS) web server. The limits in charge depend on

  • The version of IIS
  • The configuration of IIS
  • The possible usage of certain ISAPI extensions and filters
    • If so, which extensions/filters? (for example: ASP or ASP.NET)
    • If so, which versions of extensions/filters (for example: version 4.0 of ASP.NET)

Different versions of IIS and ISAPI extensions/filters have different upload limit settings and/or hardcoded upload limits. Yes, you can already feel this article, that restricts itself to IIS, ASP and ASP.NET, won’t be THAT easy… 😉
 

 

Why limiting the upload size? And which limit do I want?

The availability of upload limit settings does not mean we should enlarge or maximize those limits in any scenario: I strongly advise to set the limits according to what’s needed and nothing more, even if that’s less than what’s allowed by default. Of course it’s not always easy to determine what’s needed. For example, suppose you would allow clients to upload PDF files containing reports of whatever. How large could those PDF files be? In many cases there is no one correct, incontrovertible answer. Most of the time it’s best to determine a reasonable and acceptable “high size” you think should be enough in almost any case (for example, with a 99,999% likeliness). In some cases you should enlarge the allowed size even more, because you need a 99,99999999% hit percentage for example. And perhaps even that’s not enough and you want no limit at all: perhaps you don’t expect a lot of such requests and you rather want that one client to be able to upload a 500 GB or larger PDF file than to tell that client it’s not possible; but that’s a choice you make and then you do consider that behavior as something that’s “needed“. You see, what’s “needed” all depends on how important your service is, how much your service may cost, which resources you have (storage, bandwidth,…), how flexible you want to be, etc. etc. But if you do consider something as “not needed“, please configure IIS and ISAPI extensions/filters in such a way they don’t allow that.

Why should we configure limits?

  • This way we “harden” the environment, reducing the security risks, as the more things you allow, the higher the chance malware or a hacker could use it against you. For example, it increases the chance on a successful denial of service (DoS) attack.
  • Secondly, it also ensures performance a little bit more, as those 32649 simultaneous 2 TB uploads could easily result in bad response times J You could solve this issue (scale-up and/or scale-out your environment), but this means you need more resources and thus a higher cost (hardware, maintenance, licenses,…).
  • Thirdly, a limit enforces something you don’t want. Suppose you offer a cloud storage service where you allow users to upload files, but you don’t want them to be able to store files higher than 20 MB. Of course you could take care of that limit in a programmatic way, but as an extra layer of security you could also enforce this on IIS and ISAPI extension/filter levels.
  • Probably a few other reasons too.

 

 

Before IIS 4.0

Simple: there were no limits. Let’s move on to IIS 4.0! (Uhm, don’t think every IIS version will be dealt with so quickly J)
 

 

IIS 4.0

IIS 4.0 was released as part of an Option Pack for Windows NT 4.0.

The maximum allowed number of bytes of a request message (so the whole
HTTP message) is determined by the registry entry MaxClientRequestBuffer. By default this entry is not present and this means we get the default restrictions. By setting the reg entry explicitly and setting its value we can tune this limit. In practice it’s difficult to tell where the real limit resides (not resulting in some kind of error or conflicting with something else), as this mechanism is also depending on the buffers IIS reads the blocks into. Anyway, here are the details of MaxClientRequestBuffer:

parameter value
registry entry MaxClientRequestBuffer
key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\w3svc\parameters
data type DWORD
description maximum allowed number of bytes of an HTTP request message
presence – default Not present
minimum 0 (although that’s obviously not very practical, isn’t it?)
maximum 2147483648 (2 GiB)
default values IIS 4.0: 2 MiB
remarks IIS has to be restarted for the change to become active

If you install the IIS add-on UrlScan (technically implemented through UrlScan.dll) you get extra sets of properties. UrlScan has its own configuration file (%windir%\System32\inetsrv\UrlScan\UrlScan.ini). This file consists of sections, containing properties and their values. Version 2.5 introduces new sets of properties at the UrlScan level, one of them called RequestLimits, providing properties to restrict the size of requests. One of these properties is called MaxAllowedContentLength (not to be confused with maxAllowedContentLength, which is discussed later):

parameter value
property MaxAllowedContentLength
section RequestLimits
data type integer
description Maximum allowed number of bytes of the entity body of a request. However, it does not prevent the server from reading more data than this value. For example, if a client makes a chunk transfer encoded POST, this option doesn’t track the size of the entity in the request. So take care when counting on this setting.
presence – default present by default
minimum 0 (not very useful…)
maximum 4294967296 (4 GiB)
default values 2000000000 (almost 2 GiB)
Remarks
  • optional, but there is no implicit default
  • a restart of IIS is needed after making a change

Links:

 

 

IIS 5.0

IIS 5.0 is found in Windows 2000.

IIS 5.0 can also be extended with UrlScan 2.5 (see IIS 4.0 for more information), meaning the UrlScan property MaxAllowedContentLength is also available.

The IIS setting MaxClientRequestBuffer (see IIS 4.0 for more information) still exists here, but with a different default value:

parameter value
registry entry MaxClientRequestBuffer
registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\w3svc\parameters
registry data type DWORD
description maximum allowed number of bytes of an HTTP request message
presence – default not present by default
minimum 0 (although that’s obviously not very practical, isn’t it?)
maximum 2147483648 (2 GiB)
default values
  • IIS 5.0: 128 KiB
  • IIS 5.0 on Windows 2000 SP4: 16 KiB
remarks IIS has to be restarted for the change to become active

Because there was a need to address the upload size limit for ASP sites/applications in particular, a new setting was introduced with the October 2002 cumulative update for IIS (MS02-062, called “MS02-062: October 2002 Cumulative Patch for Internet Information Services” and assigned to KB327696 (http://support.microsoft.com/kb/327696)). Starting from this update one could control the maximum allowed number of bytes of the entity body of a request for an ASP site/application (so of an ASP request). The entity body (entity-body) is the body of the entity, which is the body of an HTTP request (the rest of the HTTP message are message headers, while the rest of the entity are entity headers). Anyway, for ASP this setting ANDs the “generic” setting (MaxClientRequestBuffer), meaning both settings apply. So it could be that a request doesn’t conflict with one of the settings, but does conflict with the other one, resulting in the request failing. All this means you should take 2 settings into account when dealing with ASP: the generic one (applying to the whole request message) and the one for “ASP only” (applying to the entity-body only of the request message (don’t forget the difference between those 2 settings!). The new setting, so the one for ASP, is not a registry entry, but a metabase property. The metabase is IIS’ configuration “database” (%SystemRoot%\system32\inetsrv\MetaBase.xml), which is a hierarchical structure of nodes, each one described by a set of properties. Each node represents an object and is of a certain type, called the admin object type. The metabase supports inheritance, so properties are inherited by child nodes from parent nodes, so they don’t have to be configured explicitly at every possible node. If you do set the same property again at a child node, it overrules the inherited property. All this means the new setting for ASP, being a metabase property, can be set on several objects, on several levels. The property I’m talking about is named AspMaxRequestEntityAllowed. Its value contains the maximum number of bytes allowed in the entity-body of an ASP (not ASP.NET!) HTTP request (so the HTTP message body minus the entity headers).

parameter value
metabase property AspMaxRequestEntityAllowed
metabase levels/objects see the following table
metabase data type DWORD
description maximum allowed number of bytes of the entity body of an ASP request
presence – default /LM/W3SVC
minimum 0 (not very useful…)
maximum 1073741824 (1 GiB)
default values 1073741824 (1 GiB) (KB327659 talks about another value, but actually that’s the default value for IIS 6.0 only)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

The levels where this property can be set are the following:

metabase path IIS admin object type
/LM/W3SVC IIsWebService
/LM/W3SVC/n IIsWebServer
/LM/W3SVC/n/ROOT

/LM/W3SVC/n/vdir

IIsWebVirtualDir
/LM/W3SVC/n/ROOT/dir_or_dirs

/LM/W3SVC/n/vdir/dir_or_dirs

IIsWebDirectory

Remarks:

  • IIsWebService refers to the whole web server
  • n is an integer and is an identifier referring to a web site (object type “IIsWebServer”, as a web site is in a way seen as a “sub” web server)
  • The root of a web site is in fact some kind of a virtual directory (vdir), hence it falls under the “IIsWebVirtualDir” category
  • dir_or_dirs is a folder path containing one or more folders
  • AspMaxRequestEntityAllowed cannot be configured for an IIsWebFile object

Just like with ASP, there was also a need to address the size of ASP.NET requests. Right from the beginning .NET Framework included a setting to achieve this, meaning this setting was already present from the very first version of ASP.NET (1.0), although it’s only valid starting from IIS 5.0, even though ASP.NET 1.0 can run on IIS 3.0 and 4.0. As IIS 5.0 is used on Windows 2000, this means the setting is supported starting from Windows 2000 with IIS. The setting, called maxRequestLength, only has influence on ASP.NET requests and not on other requests (like “generic” or ASP requests). The ASP setting mentioned (AspMaxRequestEntityAllowed) on his turn does not have impact on ASP.NET requests. But MaxClientRequestBuffer does play a role for ASP.NET requests, as it does with every HTTP request: this means an ASP.NET request is accepted only when MaxClientRequestBuffer AND maxRequestLength limits are respected.

The ASP.NET setting maxRequestLength must be configured in .NET Framework configuration files, so

  • For every ASP.NET application using a particular .NET Framework version: machine.config
    • 32 bit: in “%SystemRoot%\Microsoft.NET\Framework\vVERSION\Config” with VERSION the version number of .NET Framework
  • For a particular ASP.NET level (web application, a certain folder of a web application,…): web.config
parameter value
property maxRequestLength
levels/objects configuration/system.web/httpRuntime
data type Int32
description maximum allowed number of kilobytes of the entity body of an ASP.NET request
presence – default not present by default
minimum 0 (not very useful…)
maximum
  • for ASP.NET 1.0-1.1: 1048576 (1 GiB)
  • for ASP.NET 2.0: 2097151 (so 2147482624 bytes, almost 2 GiB)
default values 4096 (4 MiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

Links:

 

 

IIS 5.1

IIS 5.1 is the IIS version on Windows XP Professional. Other editions of Windows XP don’t contain IIS, except for Windows XP Professional x64 Edition, which is another edition of Windows XP than Windows XP Professional and thus not the same, so it doesn’t contain IIS 5.1 (but it does contain IIS 6.0).

The registry setting MaxClientRequestBuffer (see IIS 4 and 5.0 for more information) is gone here, but the metabase property AspMaxRequestEntityAllowed (see IIS 5.0 for more information) still counts starting from the October 2002 cumulative update for IIS. The ASP.NET setting maxRequestLength (see IIS 5.0 for more information) is available for every IIS 5.1 version. That’s right, before this update there was no limit for request sizes at all except for ASP.NET and starting from the cumulative update there was only a limit for ASP and ASP.NET entity bodies! The settings for IIS 5.1 have the same behavior as for IIS 5.0.

Because more .NET Framework versions are supported on Windows XP (and thus IIS 5.1) I’ve updated the table describing maxRequestLength:

parameter value
property maxRequestLength
levels/objects configuration/system.web/httpRuntime
data type Int32
description maximum allowed number of kilobytes of the entity body of an ASP.NET request
presence – default not present by default
minimum 0 (not very useful…)
maximum
  • for ASP.NET 1.0-1.1: 1048576 (1 GiB)
  • for ASP.NET 2.0-4.0: 2097151 (so 2147482624 bytes, almost 2 GiB)
default values 4096 (4 MiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

UrlScan 2.5 (see IIS 4.0 for more information) can also be installed on IIS 5.1, although there are also newer versions available (3.0 and 3.1). I’ve updated the table which details MaxAllowedContentLength:

parameter value
property MaxAllowedContentLength
section RequestLimits
data type integer
description Maximum allowed number of bytes of the entity body of a request. However, it does not prevent the server from reading more data than this value. For example, if a client makes a chunk transfer encoded POST, this option doesn’t track the size of the entity in the request. So take care when counting on this setting.
presence – default present by default
minimum 0 (not very useful…)
maximum 4294967296 (4 GiB)
default values
  • 2.5: 2000000000 (almost 2 GiB)
  • 3.x: 30000000 (almost 30 MiB)
remarks
  • optional, but there is no implicit default
  • in 2.5: a restart of IIS is needed after making a change
  • in 3.x: a restart of IIS is not needed after making a change

Links:

 

 

IIS 6.0

IIS 6.0 is to be found in Windows XP Professional x64 Edition and Windows Server 2003.

The UrlScan property MaxAllowedContentLength can be used on IIS 6.0, as UrlScan 2.5, 3.0 and 3.1 are supported for IIS 6.0 (see IIS 4.0 and 5.1 for more information). Starting from version 3.0 there is a 64 bit version available for download. This means the configuration file (UrlScan.ini) can be found in %windir%\SysWOW64\inetsrv\UrlScan. If you use 32 bit worker processes on 64 bit IIS (possible from IIS 7.0) or you use IIS in 32 bit mode (possible before IIS 7.0), the folder for UrlScan.ini stays %windir%\System32\inetsrv\UrlScan though.

The registry setting MaxClientRequestBuffer (see IIS 4 and 5.0 for more information) is, just like in IIS 5.1, still gone here (and actually it will never appear again in later versions of IIS). The metabase property AspMaxRequestEntityAllowed (see IIS 5.0 and 5.1 for more information) is also present in IIS 6.0, this time right from the start, so you don’t need an update to get this “feature”. For ASP.NET maxRequestLength (see IIS 5.0 and 5.1 for more information) is still here too.

For AspMaxRequestEntityAllowed there is a small change, related to the default value, which has been reduced:

parameter value
metabase property AspMaxRequestEntityAllowed
metabase levels/objects see the following table
metabase data type DWORD
description maximum allowed number of bytes of the entity body of an ASP request
presence – default /LM/W3SVC
minimum 0 (not very useful…)
maximum 1073741824 (1 GiB)
default values 204800 (200 KiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

maxRequestLength is a setting at ASP.NET level. Note that IIS 6.0 can run on x64 editions of Windows, so this means that starting from IIS 6.0 we can also have ASP.NET in an x64 flavor, because starting with .NET Framework 2.0 an x64 flavor exists. This means there is a small change in where maxRequestLength can be set precisely for machine.config:

  • For every ASP.NET application using a particular .NET Framework version: machine.config
    • 32 bit: in “%SystemRoot%\Microsoft.NET\Framework\vVERSION\Config” with VERSION the version number of .NET Framework
    • 64 bit: in “%SystemRoot%\Microsoft.NET\Framework64\vVERSION\Config” with VERSION the version number of .NET Framework

This doesn’t mean the story ends here. In contrast to IIS 5.1, IIS 6.0 has a new setting, i.e. the metabase property MaxRequestEntityAllowed. Its value is the maximum allowed number of bytes of the entity body of a “generic” HTTP request (so a request not targeting ISAPI extensions/filters). This means the setting does NOT target ASP requests, ASP.NET requests, etc. Otherwise said: there is 1 setting for “generic” requests, 1 setting for ASP requests and 1 setting for ASP.NET requests, so there is no change for ASP requests compared to IIS 5. Note that MaxRequestEntityAllowed has not the complete same function as MaxClientRequestBuffer: the first one targets the entity body of a request, while the latter one targets the whole message. Also, the first one is for generic requests only, while the latter one is meant for every request. Anyway, here are the details related to MaxRequestEntityAllowed:

parameter value
metabase property MaxRequestEntityAllowed
metabase levels/objects see the following table
metabase data type DWORD
description maximum allowed number of bytes of the entity body of a “generic” request
presence – default not present by default
minimum 0 (not very useful…)
maximum 4294967295 (unlimited, so the maximum limited value is 4294967294 (4 GiB – 1 byte))
default values 4294967295 (unlimited)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

The levels where this property can be set are the following:

Metabase path IIS admin object type
/LM/W3SVC IIsWebService
/LM/W3SVC/n IIsWebServer
/LM/W3SVC/n/ROOT

/LM/W3SVC/n/vdir

IIsWebVirtualDir
/LM/W3SVC/n/ROOT/dir_or_dirs

/LM/W3SVC/n/vdir/dir_or_dirs

IIsWebDirectory
/LM/W3SVC/n/ROOT/[dir_or_dirs]/file

/LM/W3SVC/n/vdir/[dir_or_dirs]/file

IIsWebFile

Remarks:

  • dir_or_dirs is optional for the category IIsWebFile
  • the levels supported are the same as for AspMaxRequestEntityAllowed, except for the fact that the latter cannot be configured for an IIsWebFile object, while that is the case for MaxRequestEntityAllowed
  • in older versions of IIS “unlimited” was typically represented by the value -1, but this has changed. For example, MaxRequestEntityAllowed supports the “unlimited” value, but it’s referred to with the value 4294967295, which is the highest theoretical number that can be set for this setting

Links:

 

 

IIS 7.0 – 8.5

IIS 7.0 is found in Windows Vista and Windows Server 2008. IIS 7.5 is there in Windows 7 and Windows Server 2008 R2. IIS 8.0 is found in Windows 8 (pre 8.1) and Windows Server 2012 (RTM). The latest version is IIS 8.5 and is bundled with Windows 8.1 and Windows Server 2012 R2.

The UrlScan property MaxAllowedContentLength can be used on IIS 7.0 through UrlScan 2.5, 3.0 and 3.1, but is not supported anymore for later IIS versions (see IIS 4.0 and 5.1 for more information). Note however that the need for UrlScan in IIS 7.0, even though it’s supported, is very little, as IIS 7.0 and later contain the IIS module “Request Filtering”, which actually takes over and even extends the functionality of the RequestLimits section of UrlScan. Even on IIS 6.0 the need for UrlScan was already minor, because of extra built-in capabilities of IIS 6.0.

Every other setting has been thrown away, except for maxRequestLength (see IIS 5.0 and 5.1 for more information). .NET Framework 4.5(.x) is supported on Windows Vista SP2 and Windows Server 2008 SP2 and later and thus on IIS 7.0 and later (on Windows 7 and Windows Server 2008 R2, it’s also supported, but SP1 is a requirement). The table is updated with the maximum value for .NET Framework 4.5(.x):

parameter value
property maxRequestLength
levels/objects configuration/system.web/httpRuntime
data type Int32
description maximum allowed number of kilobytes of the entity body of an ASP.NET request
presence – default not present by default
minimum 0 (not very useful…)
maximum
  • for ASP.NET 1.0-1.1: 1048576 (1 GiB)
  • for ASP.NET 2.0-4.0: 2097151 (2147482624 bytes, almost 2 GiB)
  • for ASP.NET 4.5(.x): 2147483647 (2 TiB), but only when the application pool’s managed pipeline mode is “Classic”; when that mode is “Integrated” the older limit of 2097151 still counts
default values 4096 (4 MiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

Starting with IIS 7.0 the IIS metabase has been replaced by other, XML based configuration files:

  • For everything that’s hosted: applicationHost.config (in %SystemRoot%\ System32\inetsrv\config)
  • For a particular level (vdir, folder,…): web.config (in the folder related to that level)

Remember ASP.NET has its own set of configuration files (machine.config and web.config; see IIS 5.0 and 6.0 for more information). The web.config files are shared between IIS and ASP.NET, so they contain the settings for both IIS and ASP.NET at the level web.config relates to. Just as the metabase contained ASP settings, ASP settings are still placed under the “IIS settings hierarchy”. (Note: if we talk about ASP in this article we actually mean Microsoft’s ASP ISAPI extension/filter, not a 3rd party one’s).

There are 2 new settings determining upload limits of every request (“generic” requests, ASP requests, ASP.NET requests, whatever), called maxRequestEntityAllowed and maxAllowedContentLength. Note that maxRequestEntityAllowed is not the same as MaxRequestEntityAllowed from IIS 6.0. Of course they are similar, but technically they are different, so the latter’s capital for the first letter DOES make a difference. Here are the details of the new settings, being XML properties:

parameter value
property maxRequestEntityAllowed
levels/objects configuration/system.webServer/serverRuntime
data type uint
description maximum allowed number of bytes of the entity body of any request; enforced by the core of IIS
presence – default not present by default
minimum 0 (not very useful…)
maximum 4294967295 (unlimited)
default values 4294967295 (unlimited)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

 

parameter value
property maxAllowedContentLength
levels/objects configuration/system.webserver/security/requestFiltering/requestLimits
data type uint
description maximum allowed number of bytes of the entity body of any request; enforced by the Request Filtering module of IIS, which is installed by default, but can be uninstalled
presence – default not present by default
minimum 0 (unlimited, so the minimum limited value is 1)
maximum 4294967295 (4 GiB – 1 byte)
default values 30000000 (approximately 28,6 MiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

Besides these XML properties there is also a hardcoded limit valid for every request’s entity body:

  • with classic managed pipeline mode: 4 GiB
  • with integrated managed pipeline mode: 2 GiB

There is also 1 new property for ASP requests (maxRequestEntityAllowed). This is NOT the same, although of course similar, than the eponymous prtoperty for the “any level”. Details are the following:

parameter value
property maxRequestEntityAllowed
levels/objects configuration/system.webServer/asp/limits
data type uint
description maximum allowed number of bytes of the entity body of an ASP request
presence – default not present by default
minimum 0 (not very useful…)
maximum 2147483647 (2 GiB – 1 byte)
default values 200000 (almost 200 KiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

All the settings overlap each other, so we have an AND scenario here: the most restricting value “wins”:

  • For “generic” requests when Request Filtering is not installed: maxRequestEntityAllowed (generic) or the hardcoded limit, whichever is the smallest
  • For “generic” requests when Request Filtering is installed: maxRequestEntityAllowed (generic), maxAllowedContentLength or the hardcoded limit, whichever is the smallest
  • For ASP requests when Request Filtering is not installed: maxRequestEntityAllowed (generic) or maxRequestEntityAllowed (ASP), whichever is the smallest (the last one is always smaller than the hardcoded limit, so we can ignore this limit in our comparison by definition)
  • For ASP requests when Request Filtering is installed: maxRequestEntityAllowed (generic), maxAllowedContentLength or maxRequestEntityAllowed (ASP), whichever is the smallest (the last one is always smaller than the hardcoded limit, so we can ignore this limit in our comparison by definition)
  • For ASP.NET requests when Request Filtering is not installed: maxRequestEntityAllowed (generic), maxRequestLength or the hardcoded limit, whichever is the smallest
  • For ASP.NET requests when Request Filtering is installed: maxRequestEntityAllowed (generic), maxAllowedContentLength or the hardcoded limit, whichever is the smallest

You could wonder why there are 3 limits for every request. maxRequestEntityAllowed is actually the new MaxRequestEntityAllowed from IIS 6.0 and is unlimited by default. The thing is IIS has a special optional module called Request Filtering, which provides IIS administrators and developers with a full blown request filtering module extending the base filtering capabilities of IIS. When this module is installed (which is the case in a default situation), another entry (maxAllowedContentLength) plays a role in the upload size limit configuration, which has a default value restricting the upload size to almost 30 MiB. So at the end this property is the one you should really use to control upload size limits at a general level IF Request Filtering is installed (which is typically the case). The module is implemented by

  • the role service “Request Filtering” of the role “Web Server” (Windows Server 2008 (R2))
  • the sub role service “Request Filtering” of the sub role service “Security” of the role service “Web Server” of the role “Web Server (IIS)” (Windows Server 2012 (R2))

Conceptually every limit has its range. maxRequestEntityAllowed and maxAllowedContentLength even support unlimited sizes (so practically spoken this means there are no limits). But at the end IIS has a hardcoded limit to really, effectively limit this size; let’s say these hardcoded limits are the “ultimate, non-configurable restrictions to keep other settings’ values under control”. The precise limit depends on the managed pipeline mode.

The settings (XML properties) above can only be configured at “lower” levels if

  • The IIS module the setting belongs to (if it belongs to an IIS module) is unlocked (meaning its delegation state is set to “Read/Write”). For the settings mentioned here this is only applicable to maxAllowedContentLength , as it’s the only setting belonging to an IIS module, i.e. “Request Filtering”. By the default this module is unlocked. If the module is locked though, settings belonging to this IIS module can only be set at the highest level and cannot be delegated (configured at lower levels). The concept I’m talking about here is called feature delegation (or configuration delegation).
  • the settings, whether part of a module or not, are allowed to be configured at a particular lower level. Even if it belongs to a module and that module is unlocked, this doesn’t mean everyone can set every of its settings (properties) at every level. The unlock (see the previous bullet) enables the possibility of allowing a property at a lower level, but only when the permission has been effectively “assigned” at that particular level.

    There are two ways to set such a “permission”:

    • in applicationHost.config a “section” element (in configuration/configSections/sectionGroup) can be used with a “name” property (with the name of an element (like serverRuntime) as its value) and an “overrideModeDefault” property. If the last one has the value “Allow” the properties of the element from the “name” property can be set at all lower levels by default. The value “Deny” forbids this by default.
    • In a config file a “location” element contains a property “path” that refers to a certain location (path) in the hierarchy of IIS (for example, a certain vdir). “location” can also have a property called “overrideMode”. If its value is set to “Allow”, this means the properties configured under this very specific “location” tag are allowed to be configured at lower levels. The value “Deny” forbids that though.

    Both mechanisms can be used at the same time. This way it’s possible that a certain property can be configured at every level by default (because overrideModeDefault for that property is set to “Allow” in applicationHost.config), except under a certain vdir (because the property is configured at that vdir level, but with an overrideMode configured there as “Deny” for that location). Note that a config file can describe properties for a particular path with multiple “location” elements; for example, a “location” element with an overrideMode “Allow” and one with an overrideMode “Deny”.

Links:

 

 

Data types

MaxClientRequestBuffer is a registry entry of the registry data type DWORD. This means it’s a signed 32 bit integer and has a value that could theoretically reside in the range of 0 till 4294967295, but the upper limit of the practical range is truncated to 2147483648 though.

The metabase properties MaxRequestEntityAllowed and AspMaxRequestEntityAllowed are of the DWORD data type in the metabase. This means they are treated as unsigned 32 bit integers at the metabase level (and IIS itself doesn’t change this). Their range is theoretically from 0 till 4294967295 (4 GiB – 1 byte, or 0xFFFFFFFF in a hexadecimal way), but for AspMaxRequestEntityAllowed the actual, useful range is truncated till 1073741824 (1 GiB) by IIS. In WMI however the values are stored in a variable of the SINT32 data type (meaning a signed 32 bit integer), so when those values are used they need to be copied to an unsigned 32 bit integer variable first.

The XML properties maxAllowedContentLength, maxRequestEntityAllowed (general) and maxRequestEntityAllowed (ASP) are uint properties, meaning they are treated as unsigned 32 bit integers and have a theoretical range of 0 till 4294967295, although the ASP property is practically limited to a maximum of 2147483647 (2 GiB – 1 byte).

maxRequestLength on the other hand is an Int32 (signed 32 bit integer) and has a theoretical range of -2147483648 till 2147483647, but the range is practically truncated from 0 till 1048576 (ASP.NET 1.x; 1 GiB), 2097151 (ASP.NET 2.0, 3.x, 4.0 or 4.5(.x) Integrated; almost 2 GiB) or 2147483647 (ASP.NET 4.5(.x) Classic; 2 TiB). The truncation in ASP.NET 1.x was present because ASP.NET health monitoring and memory fragmentation was already taking in a little more than 1 GiB of memory, in a worker process’ 2 GiB virtual address space…

 

 

Browsers

Something to keep in mind is the file upload limit of the client, typically a browser. The following table lists some limits, but beware they are upload limits, NOT download limits.

Browser Version(s) Upload limit
Internet Explorer (IE) 3-8 2 GiB – 1 byte
Internet Explorer (IE) 9-11 4 GiB
Firefox all 2 GiB – 1 byte
Chrome all > 4 GiB
Opera 10 > 4 GiB

Links:

 

 

Headers and responses

The size of the entity body to be uploaded is indicated by the HTTP entity header Content-Length. If that value is larger than what is allowed by a limit, IIS sends a response with a HTTP error code. When limits overlap, checks at the general level are performed before those of ASP, ASP.NET or other ISAPI extensions/filters. The HTTP error code is:

  • 400 (Bad Request): when MaxClientRequestBuffer is the one complaining
  • 403 (Forbidden): when AspMaxRequestEntityAllowed or MaxRequestEntityAllowed is the one complaining
  • 404 (Not Found) – more specifically 404.13, so the status code is 13 (CONTENT_LENGTH_TOO_LARGE): when maxAllowedContentLength or maxRequestLength is the one complaining

Links:

 

 

The story is more complex than this…

Besides settings and hardcoded limits for the size of a request or an entity body of a request, some other limits exist that could retain requests to be accepted. Let’s take a look at some of them. Be aware of the fact there are many other size related settings that in some way influence the success or failure of a request. It’s impossible to deal with them all, but I think I have addressed the most important and common ones on the next few pages.
 

Http.sys

From IIS 6.0 on we also have limits at a lower level, i.e. the level of the Http.sys, the kernel mode driver that handles HTTP requests. The registry entry MaxRequestBytes is such an example:

parameter value
registry entry MaxRequestBytes
key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters
data type DWORD
description maximum allowed number of bytes of the combination of HTTP request line (like “GET /pictures/pic1.jpg HTTP/1.1“) and HTTP request headers
presence – default not present by default
minimum 256
maximum 16777216 (16 MiB)
default values 16384 (16 KiB)
remarks IIS has to be restarted for the change to become active

Another example is MaxFieldLength:

parameter value
registry entry MaxFieldLength
key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters
data type DWORD
description maximum allowed number of bytes of an HTTP request header
presence – default not present by default
minimum 64
maximum 65534 (64 KiB – 2 bytes)
default values 16384 (16 KiB)
Remarks
  • if MaxRequestBytes is smaller, MaxFieldLength is adjusted
  • IIS has to be restarted for the change to become active

Links:

 

Buffer to ISAPI

IIS sends a certain piece of uploaded data to an ISAPI extension or module when necessary (for example, when using ASP or ASP.NET) and it does so via a buffer. When the buffer is filled, the ISAPI extension or module gets the data directly from the client, bypassing the buffer (but of course passing IIS of course; it just happens without an intermediate buffer). The size of the buffer is defined by the IIS property uploadReadAheadSize. The first table describes this property for IIS 4.0, 5.x and 6.0, the second table for IIS 7.0 and later. In the first case the property is a metabase property, while in the second case it’s an XML property.

parameter value
metabase property uploadReadAheadSize
metabase levels/objects same as MaxRequestEntityAllowed (see IIS 6.0 for more information)
metabase data type DWORD
description size (in bytes) of the buffer between IIS and ISAPI extensions/filters
presence – default not present by default
minimum 0 (meaning no buffer is used)
maximum 4294967295 (4 GiB – 1 byte)
default values 49152 (48 KiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

 

parameter value
property uploadReadAheadSize
levels/objects configuration/system.webServer/serverRuntime
data type uint
description size (in bytes) of the buffer between IIS and ISAPI extensions/filters
presence – default not present by default
minimum 0 (meaning no buffer is used)
maximum 2147483647 (almost 2 GiB)
default values 49152 (48 KiB)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

When using client certificates this leads to an HTTP error code 404.13 when the buffer is smaller than the length of the data to be uploaded. The cause is a deadlock.

In another situation HTTP error code 400 (Bad Request) can be returned. This is explained in KB810957.

Links:

 

Memory – recycling

Also take note of the memoryLimit ASP.NET property of the processModel element under system.web. This property determines the amount of memory a worker process can consume before it recycles; it does so under the form of a percentage of total system memory. Just don’t forget about the existence of this property, because it can limit your upload possibilities in an indirect way. Anyway, here is a quick overview of a few details:

parameter value
property memoryLimit
levels/objects configuration/system.web
data type Int32
description amount of memory a worker process can consume before it recycles; it does so under the form of a percentage of total system memory
presence – default not present by default
minimum 0 (not very useful…)
maximum 100 (100%)
default values 60 (60%)
remarks optional, meaning it shouldn’t be set explicitly, so there is an implicit default

Links:

 

Timeout

An ASP.NET property (executionTimeout) exists that could stop and fail the upload after a certain amount of seconds, so uploading content can be stopped when it takes too long:

parameter value
property executionTimeout
levels/objects configuration/system.web
data type Timespan
description maximum allowed number of seconds (the processing of) an ASP.NET request may last
presence – default not present by default
minimum 0 (not very useful…)
maximum 10675199.02:48:05.4775807
default values
  • ASP.NET 1.x: 90 (90s)
  • ASP.NET 2.x-4.x: 110 (110s)
remarks
  • optional, meaning it shouldn’t be set explicitly, so there is an implicit default
  • the property “debug” of the element “compilation” must be “false” for this setting to be enabled

Links:

 

disableMaxRequestLength

Code wise we can disable maxRequestLength: the method GetBufferlessInputStream of the HttpRequestWrapper class in ASP.NET 4.5 returns a Stream (System.IO.Stream) object that is used to read the incoming HTTP entity body. The method has 1 parameter, a boolean called disableMaxRequestLength. When this parameter is set to true, maxRequestLength is ignored (for the entity body that’s read in by that particular piece of code, that is). This way ASP.NET is able to receive content of any size, but of course that’s not completely true, as the IIS restrictions limit the allowed size too at the end. Also, disabling of maxRequestLength is only possible in a programmatic way in a per request scenario, which makes it more difficult to achieve this effect in a more global and/or administrative way.

Links:

 

WCF

ASP.NET consists of different technologies, one of them being Windows Communication Foundation (WCF), a modern web service technology introduced with .NET Framework 3.0. WCF knows the concept of a binding. One property of a binding is maxReceivedMessageSize, which limits the total size of a received message (so not just the entity body), but this time only for a WCF scenario and for a specific binding. A binding has a transfer mode (determined by the property transferMode), which defines the way the transport works: something could be saved in a buffer first before processing occurs (called buffering, which is the default), but it’s also possible to start processing before everything has been uploaded (called streaming) – actually this means the message headers are buffered, but the message body is streamed. The size of the buffer linked to a binding is determined by yet another property, maxBufferSize. With buffering the latter property should be at least a large as maxReceivedMessageSize, due to obvious reasons.

parameter value
property maxReceivedMessageSize
levels/objects configuration/system.serviceModel
data type Int64
description maximum allowed number of bytes of an ASP.NET WCF request for a specific binding
minimum 0 (not very useful…)
maximum 2147483647 (2 GiB – 1 byte)
default values 65536 (64 KiB)
remarks

 

parameter value
property maxBufferSize
levels/objects configuration/system.serviceModel
data type Int32
description number of bytes of the buffer linked to an ASP.NET WCF binding
minimum 0 (not very useful…)
maximum 2147483647 (2 GiB – 1 byte)
default values 65536 (64 KiB)
remarks

WCF sends by default base64 encoded, which enlarges the message with 33%, meaning the actual content to be uploaded must be smaller than the value of maxReceivedMessageSize. For example, when keeping its default value (65536, which is 64 KiB), this means the actual content you can upload at max is 48 KiB. Message Transmission Optimization Mechanism (MTOM) can be used to optimize the message.

Related is the setting maxBufferPoolSize. Search the Internet for more information about this limit, which targets the Buffer Manager.

These properties can be set on different types of bindings. One example, which is used for many links below, is basicHttpBinding.

Links:

Related to these bindings is the readerQuotas element which also has a few properties related to sizing of parts of the content in a WCF request: maxArrayLength (maximum number of bytes for an array; default: 16384 bytes), maxStringContentLength (maximum number of bytes for a string; default: 8192 bytes), etc.

Links:

 

Classic web services

Before WCF was introduced the web services functionality of ASP.NET could be enhanced through an add-on called Web Services Enhancements (WSE), which is now obsolete. In WSE 2.0, meant for .NET Framework 1.1, a maxRequestLength property, different from the one I’ve talked about till now, of the element “messaging” is yet another limit for uploading, but only for incoming SOAP messages (so the whole message, not just the entity body). Of course, again, IIS limits restrict the possibilities in practice… For WSE 3.0, meant for .NET Framework 2.0, the property has been renamed to maxMessageLength. Here are some specs:

parameter value
property maxRequestLength
levels/objects configuration/Microsoft.web.services2/messaging
data type signed 32 bit integer
description maximum allowed number of kilobytes of an ASP.NET SOAP request (for classic web services)
minimum 0 (not very useful…)
maximum 2147483647 (2 TiB); -1 means unlimited
default values 4096 (4 MiB)

 

parameter value
property maxMessageLength
levels/objects configuration/Microsoft.web.services3/messaging
data type signed 32 bit integer
description maximum allowed number of kilobytes of an ASP.NET SOAP request (for classic web services)
minimum 0 (not very useful…)
maximum 2147483647 (2 TiB); -1 means unlimited
default values 4096 (4 MiB)

There is also a property in WSE 2.0 and 3.0 to limit the processing time for a SOAP message (like the property described under “Timeout” above). In WSE 2.0 the property is called executionTimeout in WSE 2.0 and executionTimeoutInSeconds in WSE 3.0:

parameter value
property executionTimeout
levels/objects configuration/Microsoft.web.services2/messaging
description maximum allowed number of seconds (the processing of) an ASP.NET request may last
minimum 0 (not very useful…)
maximum -1 means unlimited
default values 90
remarks every 15 seconds this setting is checked, meaning a thread processing a SOAP message can actually last for up to the sum of this value and 15 seconds

 

parameter value
property executionTimeoutInSeconds
levels/objects configuration/Microsoft.web.services3/messaging
description maximum allowed number of seconds (the processing of) an ASP.NET request may last
minimum 0 (not very useful…)
maximum -1 means unlimited
default values 90
remarks every 15 seconds this setting is checked, meaning a thread processing a SOAP message can actually last for up to the sum of this value and 15 seconds

Links:

 

 

Extra background infomation

Before IIS 7 if content has to be uploaded and IIS limits allowed it, IIS lets the upload finish completely, regardless to ASP.NET’s upload byte limits. ASP.NET limits were only enforced AFTER IIS has pulled in everything. Luckily this behavior has changed with IIS 7.

Thus (lack of or presence of) cooperation between IIS and ASP.NET influences how uploading works, i.e. when uploading occurs related to ASP.NET’s upload byte limits. The property uploadReadAheadSize determines when ASP.NET gets the content in its buffer and if that happens directly or through an IIS buffer. With WCF transferMode determines at what time processing takes place.

With ASP.NET 1.0 and 1.1 the buffer gets filled in memory; later on this could be streamed to disk too. This explains why maxRequestLength was truncated to 1 GiB in ASP.NET 1.x (see above for more information). It also explains why this restriction was removed starting from ASP.NET 2.0: because streaming to disk became possible now.

We should notice the ability of ASP.NET to receive content with a maximum of 2 TiB (see maqRequestLength), but again this is capped by the IIS limits. In practice this means we can receive content in ASP.NET up to 4 GiB minus 1 byte (higher is not allowed by IIS). However, this is only possible with ASP.NET 4.5(.1) applications (so only on IIS 7 and higher) with a Classic managed pipeline mode. In this mode IIS behaves like IIS 6 and uses 2 pipelines to the ASP.NET ISAPI extension (aspnet_isapi.dll) and filter (aspnet_filter.dll): one for native code and one for managed code. With integrated mode however only one pipeline is used and ASP.NET is also more integrated with IIS (with classic mode ASP.Net is more seen as just an “external” plug-in). Integrated mode is more efficient and performing; on the other hand some legacy applications still require classic mode. This means we can get large content (larger than 2147482624 bytes, the maximum maxRequestLength before ASP.NET 4.5) only for ASP.NET 4.5 applications running in a legacy mode… That’s certainly a major drawback!

There are some alternative ways to achieve your goals. For example, you could provide users with the ability to split their content to be uploaded (if possible in a transparent way of course). Then you need to reassemble the pieces at the server side.

As an alternative you can use a 3rd party uploader that provides extra features you want and/or bypasses the built-in upload size checking. Examples are SlickUpload (http://slickupload.com) for ASP.NET and Huge ASP from Motobit Software (http://www.motobit.com/help/scptutl/upload.asp) for ASP. They are so-called uploaders.
 

 

Software

IIS Limit Check is a tool that can be run on an IIS system and provides you with some limits for generic (IIS 6.0) or general (IIS 7+) and ASP requests, at different levels. The tool is actually an HTA released by Motobit Software.

Links:

 

 

Links

Some general links about the topic:

 

 

Conclusions

Before IIS 4.0 no limits were enforced and this all changed with IIS 4.0, where a default of 2 MiB was introduced, which could be increased to a maximum of 2 GiB, no matter which kind of request (“base traffic” (when no ISAPI extensions or filters are involved), ASP, ASP.NET,…). With IIS 5.0 the default was lowered to 128 KiB and even 16 KiB (Windows 2000 SP4), but only for “base traffic”, as ASP got its own setting with a default and maximum of 1 GiB, while ASP.NET was also configured separately with a default of 4 MiB and a maximum of 1 GiB (ASP.NET 1.x) or almost 2 GiB (ASP.NET 2.0). It’s clear the defaults became strict, a little bit looser for ASP.NET and very loose for ASP.

IIS 5.1 also supports ASP.NET 3.x and 4.0, but with the same default and maximum as ASP.NET 2.0. For ASP nothing changed, but for “base traffic” the limit was removed!

IIS 6.0 itself doesn’t limit the upload size of content (like files) for “base traffic” (there is a setting, but it’s configured not to restrict), but ASP does (200 KiB, which is MUCH lower than the previous 1 GiB!), just as ASP.NET (4 MiB, so no change here). Luckily we can augment this for ASP and ASP.NET to 1 GiB (with ASP.NET 1) or almost 2 GiB (with newer ASP.NET versions); again, no change here. At the same time we can put a limit on “base traffic size” again (4 GiB – 2 bytes or lower).

IIS 7 and higher has overlapping settings: the most strict one wins. At the end “base traffic” size is limited to approximately 28,6 MiB (huge change, isn’t it?), but it does so with a second setting (the other one is still set at unlimited, but again, the most strict one wins). IIS also has a hardcoded limit: 2 GiB with the integrated managed pipeline mode (and that’s the mode we want) or 4 GiB with the classic managed pipeline mode. The 28,6 MiB barrier can be increased to unlimited, so that means the actual highest allowed size for an entity body is 4 GiB.

ASP further restricts this to almost 200 KiB, while ASP.NET keeps the 4 MiB restriction. Again, we can augment this to almost 2 GiB (2 GiB – 1 bytes for ASP and a little bit less for ASP.NET), and even to 4 GiB for ASP.NET 4.5(.x) when Classic managed pipeline mode is used (well, 2 TiB actually, but IIS truncates that to 4 GiB).

Of course my conclusions don’t take into account Web Services Enhancement (WSE) and especially UrlScan, so the situation gets a little bit more complex when these add-ons are involved too. UrlScan provided older IIS versions with extra possibilities related to large uploads.

Nowadays there are still some limiting factors, even when 1st and 3rd party add-ons are used:

  • IIS: 4 GiB
  • ASP: 1 GiB
  • ASP with 3rd party uploader: 4 GiB
  • ASP.NET: almost 2 GiB, except when a legacy mode is used (2 TiB)

 

 

It’s VERY difficult to find complete and unambiguous information about this topic. You cannot believe how much wrong information I have read! So I’ve done my best to collect as much information as possible and find out the right values and theory. Please don’t hesitate to extend this article with your comments.
 

Have a good time!
 

Pedro

Advertisements

3 thoughts on “Upload large content with Http.sys, IIS, ASP and ASP.NET

    Jean-Marc said:
    01/07/2014 at 08:47

    This ist the best article about this theme in the whole internet.

    Many thanks!!

    radudee2 said:
    12/01/2016 at 05:23

    Indeed, most comprehensive and easy to follow I’ve seen as well

    Mike Q said:
    04/09/2017 at 22:59

    Thank you so much for explaining clearly the difference between maxRequestEntityAllowed and maxAllowedContentLength in newer IIS versions. This is not documented well by Microsoft at all (just like many of their other settings), and most server guides online don’t bother to clarify either, always referring to one setting or another.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s