AWS · PHP Development Notes

Things you should know about file upload handling in PHP

A note on this page: the video above was recorded some years ago, but the written content below was updated in August 2026 to reflect current practice. The fundamentals of safe file uploads have not changed, so the video and the notes cover the same ground; use them together to review your own upload code.

A file upload is one of the few features where you hand a stranger a way to put a file onto your server. Get it wrong and the path from “user input” to “code running on your box” is very short. Almost every upload vulnerability comes from trusting something the user controls: the filename, the reported type, or where the file ends up. Here is what actually keeps uploads safe, whether you are writing plain PHP or using a framework.

Validate the real type and size, not the label

The browser tells you a file’s type and name, and an attacker can set both to anything. Do not trust the extension or the reported MIME type on their own; check the actual contents. In plain PHP that means inspecting the file with something like finfo rather than believing $_FILES['file']['type']. Enforce a maximum size as well, so a huge upload cannot exhaust disk or memory. Validate against an allow-list of the few types you actually accept, never a block-list of the ones you happen to think of.

Never trust the original filename

The filename is user input. It can contain path traversal sequences, null bytes, or a second extension designed to trick your server into executing it. Generate your own safe name on the server, for example a random identifier plus an extension you derived from the validated type. Keep the original name, if you need it at all, only as a display label stored separately, never as the name on disk.

Store uploads where they cannot execute

The single most important rule: an uploaded file must never be executable as code. Store uploads outside the web root, or on a storage disk that does not run PHP, so that even if someone uploads a script it can never be requested and run. If files must live under a public directory, configure the server so that directory cannot execute anything. This is what turns a malicious upload from a remote-code-execution hole into a harmless file sitting on disk.

Serve files back through your own code

When users download their files, do not link straight to the stored path. Serve them through a controller that checks the user is allowed to see the file, sets a correct and safe content type, and sends it as an attachment where appropriate. That keeps authorisation in your hands and stops the browser from being tricked into interpreting a file as something dangerous.

The same rules in Laravel

A framework makes the safe path convenient, but the principles are identical. Laravel’s validation expresses them directly:

$request->validate([
    'document' => 'required|file|mimes:pdf,docx|max:5120', // 5 MB, real type
]);

// store with a generated name, on a disk outside the web root
$path = $request->file('document')->store('documents', 'private');

The mimes rule checks the real type, max caps the size, store() generates a safe name, and the private disk keeps the file out of the public web root. Every one of those maps back to a rule above.

Related