What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →A file upload form is one of the riskiest features you can build. How to validate real file types, store files safely, upload straight to object storage, and serve user content without opening holes.
"Let users upload a profile picture" sounds like a twenty-minute task. It's actually one of the riskiest features in a web app. An upload form accepts arbitrary bytes from strangers and stores them on your infrastructure. Get it wrong and you're hosting malware, serving cross-site scripting, or paying for someone else's 50GB of files.
Here's how to do file uploads safely, and in a way that scales.
Client-side checks (accept="image/*", file size checks in JavaScript) are for user experience only. Anyone can bypass them with curl. Every rule must be enforced on the server.
The filename extension is just text the user controls, and the Content-Type header is set by the client. Neither proves anything.
Instead, check the file's magic bytes, the signature at the start of the file:
89 50 4E 47FF D8 FF25 50 44 46 (%PDF)Libraries such as file-type (Node.js) or python-magic do this for you. Use an allow-list of types you accept, never a block-list of types you reject.
client_max_body_size)Without limits, a single request can fill your disk or exhaust memory.
User-supplied filenames can contain path traversal (../../etc/passwd), special characters, extremely long names or names that collide with existing files.
a3f1c9e2-....png.Don't save uploads into a folder your web server executes or serves as code. If someone uploads shell.php into a directory that runs PHP, they now control your server.
Better: use object storage, such as Amazon S3, Cloudflare R2, Google Cloud Storage, Backblaze B2, or Supabase Storage. Benefits:
Routing large files through your API server wastes bandwidth and memory, and serverless functions often have small request body limits. Instead:
photo.png, 1.2MB."Your server handles a few small JSON requests while the storage service handles the heavy lifting. Most object storage providers support this pattern.
Because the file bypasses your server, verify it after upload: check the object's real size and type, and process or scan it asynchronously before marking it as approved.
For image uploads, re-encode them with a library like sharp (Node) or Pillow (Python):
SVG files can contain JavaScript. If you serve a user-uploaded SVG from your main domain and someone opens it directly, that script runs with your site's origin. That's stored XSS.
Options:
usercontent.yoursite.net) so scripts can't touch your main site's cookies or storage.The same goes for HTML files and anything else a browser might render.
Content-Type and add X-Content-Type-Options: nosniff so browsers don't guess.Content-Disposition: attachment for files meant to be downloaded rather than viewed.If users share files with other users (documents, attachments), scan uploads for malware, with ClamAV or your cloud provider's scanning service, before making them available. Run scanning asynchronously and keep files in a "pending" state until they pass.
Content-Type, nosniff and signed URLs for private filesFor the rest of your app's security posture, work through the web application security checklist.
Check the file's magic bytes on the server against an allow-list. Don't rely on the extension or the Content-Type header, since both are controlled by the client.
Usually not. Store files in object storage and keep metadata (owner, path, size, type) in the database. It's cheaper, faster and easier to serve through a CDN.
A temporary URL generated by your server that lets a client upload or download one specific object directly to or from storage, without exposing your storage credentials.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →