Monday morning, and your API is pinned at 100% CPU. Nobody uploaded anything huge. There’s no 4K video sitting in the queue, no multi-gigabyte archive. Just 60,000 tiny files, a handful from enthusiastic customers batch-uploading thumbnails, and a few thousand more from a script that’s testing how far your endpoint bends.
Uploading many small files denial of service scenarios rarely start as attacks. They usually start as a power user with a folder of 4,000 icons, or an integration partner that decided to sync every asset it owns in one go. The danger isn’t the bytes. It’s everything your server does per file, multiplied by a number that got out of hand.
Uploading many small files becomes a denial of service risk when per-file overhead, connections, auth checks, disk metadata writes, and scan jobs multiply faster than payload size. A thousand 10KB files can cost more than one 10MB file. Defences include batch limits, rate limiting, queued ingestion, and offloading uploads to a managed pipeline such as Filestack that absorbs the fan-out before it reaches your servers.
This article walks through why small files hit harder than their size suggests, where the attack surface actually lives, and the layered defences: limits, rate shaping, queues, and asynchronous scanning, that keep an upload endpoint standing under pressure.
Key Takeaways
- Per-file fixed costs (auth checks, DB writes, storage PUTs, scan jobs), not raw byte count, are usually what breaks an upload endpoint first.
- A thousand 10KB files can cost your infrastructure more than a single 10MB file, because every file drags its own overhead along with it.
- Archive uploads need expansion-ratio caps; an unzipped “small” file can balloon into gigabytes of decompression work.
- Batch limits, per-account rate limiting, and queued ingestion are the first line of defence and don’t require rearchitecting your stack.
- Moving ingestion to a managed file uploader relocates the fan-out entirely, so your API sees metadata events instead of raw byte streams.
Let’s start with the part that trips most teams up: why small files are, counterintuitively, the more expensive problem.
Why Small Files Hurt More Than Big Ones
If you’re wondering what are the best methods to upload multiple files at once in a web application, the honest answer starts with a warning: multi-file upload is where fixed-cost overhead becomes visible for the first time. A single request has one TLS handshake, one auth check, one database write, one storage call. A thousand-file batch has a thousand of each, even if the total payload is identical.
The crossover point is easy to miss because it isn’t about total size at all. Object storage providers bill PUT requests per operation, separately from bytes stored, so a million tiny files can genuinely cost more in request fees than in storage. The same logic applies to your own compute: a database write or a virus-scan job doesn’t get cheaper because the file behind it is small. Once you see the pattern, it’s clear that file count deserves the same scrutiny as file size.
Small files aren’t the whole story, though. The same overhead pattern shows up in more deliberate ways once you start thinking about upload endpoints as attack surface.
The Attack Surface, Fan-Out and Amplification
Ask how can I prevent file upload vulnerabilities in my web application, and per-file overhead is only half the answer; the other half is amplification. A handful of small, cheap uploads can trigger disproportionately expensive work downstream.
Archive uploads are the clearest example. A 2MB zip file looks harmless at the network layer, but if it decompresses into 4GB of nested files, every downstream step: storage, scanning, indexing, inherits that expansion. Left unchecked, this is the classic zip-bomb pattern: a small input engineered to produce enormous output. Deep archive trees (folders inside folders inside folders) create a similar problem for anything that walks the file structure recursively.
Scan-job amplification follows the same shape. If every uploaded file queues a virus scan synchronously, a burst of a few thousand small files can back up your scanning workers even though none of them are individually suspicious. And metadata-write storms, a database row or search-index update per file, can degrade a shared database well before storage or bandwidth becomes the bottleneck.
If you’re also asking how do I detect and block malicious files during upload, the practical answer is to combine content-type verification, archive expansion limits, and asynchronous scanning (more on that in a moment) rather than relying on any single check. It’s worth noting that the line between “abuse” and “legitimate burst” is often blurry; a real customer syncing a large media library looks a lot like an attack until you’ve built the throttles that treat both cases the same way.
That overlap is actually good news operationally: the same defences that stop an attacker also stop a well-meaning customer from accidentally taking your API down. Here’s what that layered defence looks like in practice.
Defences, Limits, Rate Shaping and Queues
The first layer is the simplest: batch caps and per-account rate limits, enforced before a request does any real work. A sensible file-count cap per batch (say, 100 files per request) turns an unbounded upload into a predictable, budgetable unit of work. Layer a token-bucket rate limiter per account on top, and a single client, malicious or just enthusiastic, can’t monopolise your ingestion capacity.
If you’re using Filestack’s Picker, you can enforce upload limits with options such as maxFiles*,* minFiles*, and related* file-count controls before the upload even begins.
The second layer is queued ingestion. Instead of processing every file synchronously inside the request/response cycle, accept the upload, write a lightweight acknowledgement, and let a queue smear the actual processing over time. This is the difference between a burst that spikes your CPU for ten seconds and a burst that quietly drains over ten minutes without anyone noticing.
Speed expectations matter here too. If you’re comparing which platforms support the fastest bulk uploader options, the honest tradeoff is that raw speed and abuse-resistance pull in opposite directions; a platform optimised purely for throughput without rate shaping is also the one most exposed to fan-out abuse. The platforms that hold up under both legitimate bulk traffic and adversarial bursts are the ones that queue by design, not the ones that simply accept everything as fast as possible.
Limits and queues buy you time and predictability. What you do with that time, specifically, how you scan what’s coming in, is the next piece.
Scanning Without Melting
If you’re figuring out how can I add virus scanning to file uploads in my application, the short version is: never run it synchronously on the request path. A scan that blocks the upload response until it completes means your scanning capacity is your upload capacity, and a burst of files instantly becomes a burst of blocked requests.
The more resilient pattern is quarantine-then-release: accept the file, store it in a location that isn’t yet accessible to end users, queue an asynchronous scan job, and only promote the file to “available” once the scan clears. This decouples upload throughput from scan throughput entirely, so a scanning backlog degrades gracefully (files take longer to become available) instead of catastrophically (uploads start failing).
If you’re implementing malware detection or security policies, Filestack’s Security documentation covers built-in virus scanning, content validation, and upload security features in more detail.
Expansion-ratio caps belong here too. If an archive’s uncompressed size exceeds some multiple of its compressed size, say, 100x, reject or flag it before extraction runs to completion. It’s a small check that closes off the zip-bomb path discussed earlier, and it costs almost nothing to enforce.
Building and maintaining all of this — batch caps, rate limiters, queues, asynchronous scanning, expansion checks, is real infrastructure work. It’s worth being clear-eyed about what it takes to run in-house before deciding whether to build it yourself.
The Managed Route, Move the Blast Radius
Everything above reduces the damage a flood of small files can do once it hits your infrastructure. The strongest structural defence is not absorbing the fan-out at all: a managed file uploader terminates the file traffic upstream and hands your API a stream of metadata events instead of raw byte streams.
Filestack’s upload pipeline runs on infrastructure built to absorb this kind of fan-out; file count and rate limits are enforced in the picker before the network is even touched; virus detection runs inline in the pipeline rather than queuing on your own workers, and your servers only receive webhooks once a file is safely ingested and checked. Your application never has to reason about 1,000 simultaneous PUT requests, because it never sees them.
For an IT director at a startup company asking what’s the most secure way to manage hundreds of file uploads, the calculus is straightforward: every control described in this article: caps, throttles, queues, scanning, has to be built, tuned, and maintained somewhere. Relocating that surface to a system designed for it is less about outsourcing effort and more about outsourcing blast radius.
Whichever direction you take, build it in-house or hand off the fan-out, the underlying principle doesn’t change.
Conclusion: Count Files, Not Just Bytes
The instinct to watch for “big” uploads is understandable, but it misses where most upload endpoints actually break. Cap file counts per batch, shape request rates per account, queue ingestion so bursts smear over time, and scan asynchronously so a backlog degrades instead of cascading. Where the fan-out is large or unpredictable enough, relocating ingestion to a managed pipeline like Filestack removes the problem from your infrastructure entirely.
If you haven’t audited your own upload endpoint against these checks, start with the layered defences in the “Defences, Limits, Rate Shaping and Queues” section above; file-count caps and rate limiting alone catch most of the risk with the least amount of new infrastructure. And if you’d rather not build and maintain that stack yourself, Filestack’s upload pipeline handles the caps, queueing, and scanning for you.
FAQ
How can small files cause a denial of service?
Per-file fixed costs: auth checks, database writes, storage requests, scan jobs, multiply with every file added to a batch. Thousands of tiny files can out-cost a single large file even though the total bytes transferred are far smaller.
What limits should an upload endpoint enforce?
At minimum: file-count caps per batch, per-account rate limits, and archive expansion-ratio caps to prevent zip-bomb-style decompression attacks.
Does a managed uploader help?
Yes. Filestack terminates upload traffic upstream, so your API receives metadata events rather than raw byte streams, removing the fan-out from your infrastructure entirely.
This article was published on the Filestack blog.


Top comments (0)