How to test file size limits

Where upload limits live, what the common defaults are, and how to test a boundary to the byte.

Published by SampleTestFiles. About 4 minutes to read.

"The upload limit is 10 MB" sounds like one fact. In a real deployment it is several limits, enforced in different places, measuring different things. Testing them properly means knowing which layer said no.

What a limit actually measures

Three numbers are easily confused:

  • File size: the bytes of the file on disk.
  • Request body size: the file plus whatever wraps it. In a multipart form that is a boundary line and a few headers for each part, typically 150 to 300 bytes for a single file.
  • Encoded size: if the file is sent as base64 inside JSON, it is 33 percent larger than the original.

Web servers and proxies limit the request body. Application code usually limits the file. So a file of exactly 1 MB passes an application check of "1 MB or less" and still fails an nginx limit of 1 MB, because the request around it is slightly bigger.

Megabytes are ambiguous

A megabyte can mean 1,000,000 bytes or 1,048,576 bytes. Server configuration almost always uses the binary value: 1m in nginx and 1MB in Spring Boot both mean 1,048,576. Marketing copy and some application code use the decimal value.

All sized files on this site are binary multiples. The 1 MB samples are 1,048,576 bytes, which makes them 4.9 percent larger than a limit defined as one million bytes. If your limit is decimal, a "1 MB" sample is correctly rejected.

Default limits worth knowing

These are the defaults at the time of writing. Check the documentation for your version before relying on them.

LayerDefault limitSetting
nginx1 MB request bodyclient_max_body_size
Apache httpd 2.4.54 and later1 GB request bodyLimitRequestBody
PHP2 MB per file, 8 MB per POSTupload_max_filesize, post_max_size
Express body parsers100 KBlimit option
Next.js API routes and Server Actions1 MBsizeLimit, bodySizeLimit
Spring Boot1 MB per file, 10 MB per requestspring.servlet.multipart.*
ASP.NET Core (Kestrel)30,000,000 bytesMaxRequestBodySize
Amazon API Gateway10 MB payloadfixed
AWS Lambda (synchronous)6 MB payloadfixed
Cloudflare proxy, Free and Pro plans100 MBplan limit

The pattern to notice: the default is nearly always smaller than what a product wants to allow, and each layer has to be raised separately.

Raising the limits

To accept 20 MB files, every layer must allow a little more than 20 MB.

# nginx: applies to the whole request body
client_max_body_size 25m;
; php.ini: post_max_size must be larger than upload_max_filesize
upload_max_filesize = 20M
post_max_size = 25M
// Express with multer: limit the file itself, in bytes
const upload = multer({ limits: { fileSize: 20 * 1024 * 1024 } });
# Spring Boot
spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=25MB

Leave headroom in the outer layers. If nginx and the application both stop at exactly 20 MB, nginx will reject a 20 MB file first, and the user will see a bare 413 page instead of your error message.

A test procedure

  1. List the layers. Draw the path from the browser to storage and write the configured limit next to each hop.
  2. Send a file below the lowest limit. It must succeed. Use the 100 KB or 1 MB sample.
  3. Send a file above the intended limit. It must fail with your application's own message.
  4. Probe between. If the limit is 5 MB, the 5 MB sample shows how the edge is handled. Uploaded through a form, it will be a few hundred bytes over a body limit of the same value.
  5. Identify who refused. Look at the response. A JSON error in your API's format came from the application. A plain HTML page titled "413 Request Entity Too Large" came from a proxy or web server.
  6. Check when the refusal happens. A well-behaved server rejects an oversized upload as soon as it sees the Content-Length header, not after receiving the whole body.

This command sends a file and reports the status code and how many bytes were uploaded before the server answered:

curl -sS -o /dev/null -w '%{http_code} after %{size_upload} bytes\n' \
  -F 'file=@sample-bin-10mb.bin' https://your-app.example/upload

If the status is 413 and the uploaded byte count is far below the file size, the server refused early, which is what you want.

Test the client as well

Client-side checks exist for the user's benefit and are not security controls, but they should agree with the server. Select an oversized file and confirm that the message appears before any upload starts, that it states the limit, and that it uses the same unit the user's operating system shows.

Do not forget the minimum

The zero-byte file is a size limit test too. So is a file smaller than a format's minimum: a 20-byte "image" cannot be a real image. Decide what should happen and check that it does.

When you need another size

The samples cover the common limits. For any other value, the guide to creating test files has one-line commands that produce a file of an exact byte count on Linux, macOS and Windows.

Files used in this guide

Sample files referred to in this guide
FileFormatSizeContentsDownload
100 KB BIN samplesample-bin-100kb.bin BIN 100 KB102,400 bytes Pseudo-random bytes Download BIN
1 MB BIN samplesample-bin-1mb.bin BIN 1 MB1,048,576 bytes Pseudo-random bytes Download BIN
5 MB BIN samplesample-bin-5mb.bin BIN 5 MB5,242,880 bytes Pseudo-random bytes Download BIN
10 MB BIN samplesample-bin-10mb.bin BIN 10 MB10,485,760 bytes Pseudo-random bytes Download BIN
Empty file (0 bytes)empty-file.txt TXT 0 B0 bytes Lines: 0 Download TXT

Related guides