Why Classic ASP Uploads Are Slow, and What Actually Helps
Five causes, four of which are not your server — and how to tell which one you have.
Measure before you tune
"The upload is slow" has at least five unrelated causes, and four of them are not the server. Find out which one you have before changing anything:
| Test | What it tells you |
|---|---|
| Upload the same file from the same machine twice | Wildly different times mean the network, not your code |
| Compare a 1 MB and a 100 MB file | If time scales linearly, you are bandwidth-bound — that is normal |
| Upload from the server to itself | Removes the network entirely; slow here means server-side work |
| Watch the worker process memory | A spike the size of the file means you are buffering, not streaming |
| Time the gap after the bar hits 100% | That gap is post-upload processing, not transfer |
Most reported "slow uploads" turn out to be a visitor's asymmetric connection. A typical home line uploads at a fraction of its download speed — a 500 MB file over a 10 Mbit upstream takes about seven minutes no matter what your server does. When that is the answer, the fix is honest progress reporting, not tuning.
The one server-side mistake that matters
Everything else on this page is a rounding error next to this: Request.BinaryRead pulls the
entire request body into memory before your script runs, and parsing it allocates again.
binData = Request.BinaryRead(Request.TotalBytes) ' the whole file, in RAM
Under concurrency this is what actually kills throughput. Ten simultaneous 200 MB uploads is 2 GB of allocation before anything is saved; the pool hits its memory recycle limit and drops every request in flight, including the ones that were fine.
Reading the body incrementally, or chunking it, keeps peak memory at one buffer regardless of file size, so throughput stays flat as concurrency rises instead of collapsing at a threshold. See chunked and resumable upload.
Disk
- Do not cross a volume boundary at the end. Moving a file within one volume is a
metadata update; moving it across volumes copies every byte. Put the temp folder and the destination
on the same drive and
MoveTobecomes instant. - Do not write across the network on the hot path. Write locally, replicate afterwards — see uploading to a network share.
- Watch folder sizes. NTFS directory enumeration degrades once a folder holds hundreds of thousands of entries. Shard by date.
- Exclude the upload folders from real-time antivirus scanning, or every write waits for a scan. Scan on a schedule instead.
Post-upload work
The gap between "bar full" and "page responds" is work you are doing after the transfer: a database insert, a virus scan, image processing, a notification email. Users read that gap as a hang.
- Do the minimum synchronously — record the file and return.
- Push slow work to a queue or a scheduled task.
- If it must be synchronous, say so on screen.
UploadProcessingMsgexists for exactly this moment.
uploader.UploadProcessingMsg = "Uploaded. Processing on the server, nearly done..."
Client-side wins
The fastest upload is the one you do not make:
- Resize images in the browser. A phone photo is often 5 MB and needed at 200 KB. That is a 25× reduction before anything is sent — no server tuning comes close. See the image upload guide.
- Reject early. A file that violates your size or type rules should never leave the
machine.
MaxSizeKBandAllowedFileExtensionsare enforced in the browser. - Start on selection, not on submit. Uploading while the user fills in the rest of the form hides the transfer time completely.
IIS and infrastructure
| Setting | Why it matters |
|---|---|
| App pool idle time-out | Set to 0 — a recycle mid-transfer looks like a hang |
Server.ScriptTimeout | Too low and long uploads die at a fixed elapsed time |
uploadReadAheadSize | Can cause stalls over HTTPS or behind some modules |
| HTTPS | Modest CPU cost, and worth it regardless; on modern hardware it is not your bottleneck |
| CDN or proxy | Adds a hop, and imposes its own body-size limit |
A note on CDNs: they accelerate downloads, not uploads. Traffic still has to reach your origin, and the extra hop can make uploads marginally slower while adding a request-size cap. That is not an argument against using one — just do not expect it to speed uploads up.
What to do, in order
- Confirm it is not simply the visitor's upstream bandwidth.
- Stop buffering whole files — this is the big one.
- Resize and reject on the client.
- Keep temp and destination on the same volume.
- Move post-upload work off the request.
- Exclude upload folders from real-time scanning.
- Set the idle time-out to 0 and raise the script timeout.
Frequently asked questions
Why are my Classic ASP uploads slow?
Usually the visitor's upstream bandwidth. Server-side, the common cause is buffering the whole file in memory, which collapses under concurrency.
Does chunking make uploads faster?
Not for a single file — slightly slower, from per-request overhead. It makes them far faster under load, because memory stays flat.
Why does the progress bar reach 100% and then pause?
That pause is post-upload work on the server. Move it off the request, or tell the user it is happening.
Skip the plumbing
ASP Uploader does everything on this page out of the box: multi-file selection, a real progress bar, client and server validation, incremental large-file handling, and no COM component to register. Drop the folder on your server and add one include line.