"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.
| Layer | Default limit | Setting |
|---|---|---|
| nginx | 1 MB request body | client_max_body_size |
| Apache httpd 2.4.54 and later | 1 GB request body | LimitRequestBody |
| PHP | 2 MB per file, 8 MB per POST | upload_max_filesize, post_max_size |
| Express body parsers | 100 KB | limit option |
| Next.js API routes and Server Actions | 1 MB | sizeLimit, bodySizeLimit |
| Spring Boot | 1 MB per file, 10 MB per request | spring.servlet.multipart.* |
| ASP.NET Core (Kestrel) | 30,000,000 bytes | MaxRequestBodySize |
| Amazon API Gateway | 10 MB payload | fixed |
| AWS Lambda (synchronous) | 6 MB payload | fixed |
| Cloudflare proxy, Free and Pro plans | 100 MB | plan 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=25MBLeave 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
- List the layers. Draw the path from the browser to storage and write the configured limit next to each hop.
- Send a file below the lowest limit. It must succeed. Use the 100 KB or 1 MB sample.
- Send a file above the intended limit. It must fail with your application's own message.
- 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.
- 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.
- Check when the refusal happens. A well-behaved server rejects an oversized upload as soon as it sees the
Content-Lengthheader, 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/uploadIf 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.