SyncTrix logoSyncTrix
All articles
Platform10 min read

File uploads at scale: direct-to-storage, validation and the malware problem

Proxying uploads through your application server is the default and the wrong answer. It also puts unvalidated files inside your trust boundary.

By Priya Iyer
File uploads at scale: direct-to-storage, validation and the malware problem

The obvious implementation - browser posts the file to your API, your API writes it to storage - works until files are large or users are many. Then your application servers are spending their time and memory shuffling bytes, uploads fail on flaky connections with no resumption, and every uploaded file has passed through your application before anyone checked what it was.

01Upload directly to storage

Your backend issues a short-lived presigned URL with constraints on size and content type, and the browser uploads straight to object storage. Your servers handle a small request instead of the whole file, memory pressure disappears, and the storage provider deals with the transfer.

The client then notifies your backend that the upload completed, and you verify server-side that the object actually exists and matches expectations before recording it. Trusting the client's completion callback without verifying is how you end up with database rows pointing at objects that were never uploaded.

Through the APIDirect to storage
Server memoryProportional to file sizeNegligible
Server bandwidthFull file, twiceNone
Large file supportPoorNative, with multipart
ResumabilityMust be builtProvided by the storage API
Validation before storagePossibleMust happen after
Proxied versus direct upload

02Constrain the presigned URL properly

A presigned URL is a capability. Scope it tightly: a short expiry measured in minutes, a maximum content length, an allowed content type, and a key path the user cannot influence. Letting the client choose the object key is how one user overwrites another's file.

Generate the key server-side from a tenant identifier and a random component. Never derive it from the user-supplied filename, which is also the class of bug that produces path traversal in the storage layer.

03Validate the content, not the extension

The filename and the declared content type are both user input and both trivially forged. Determine the real type by inspecting the file's magic bytes after upload, and reject anything that does not match what was declared.

For images, re-encoding is the strongest validation available: decode and write out a fresh file. It strips embedded metadata, discards anything hidden after the image data, and produces a file you generated rather than one a stranger did. It also fixes the orientation problems that come from mobile cameras.

LayerCatchesCost
Size limit on the presigned URLStorage exhaustionFree
Magic byte inspectionForged extensionsLow
Re-encode imagesEmbedded payloads, metadataCPU
Malware scanningKnown malicious filesModerate, adds latency
Serve from a separate domainStored XSS via uploaded HTMLFree, DNS only
Validation layers worth having

04Quarantine before it is reachable

If users can upload files that other users download, you are hosting content you did not create for people you cannot vet. Upload to a quarantine bucket, scan asynchronously, and move to the serving bucket only on a clean result.

Serve user content from a separate domain, never from your application's origin. An uploaded HTML file served from your main domain executes with your cookies available to it, which turns a file upload feature into stored cross-site scripting.

05Multipart and resumption for anything large

Above roughly a hundred megabytes, a single request is a bad bet - one dropped connection and the whole transfer restarts. Multipart upload splits the file into chunks that can be retried individually and resumed after an interruption, which on mobile networks is the difference between working and not.

Set lifecycle rules to abort incomplete multipart uploads after a few days. Orphaned parts consume storage indefinitely and do not appear in a normal object listing, which makes them a cost line that nobody can account for.

Topics

file upload architecturepresigned url uploadlarge file upload handlingupload security validations3 direct upload

Priya Iyer

Staff Engineer · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.

Talk to an engineer