Handling File Uploads in PHP: A Beginner’s Guide
Learn to process file uploads in PHP securely. We cover the $_FILES superglobal, file type validation, and storing files safely in your MVC application.

Previously in this course, we explored Working with JSON APIs to handle data interchange. This lesson adds the ability to accept binary files from users, a common requirement for profile pictures, documents, or media assets.
The $_FILES Superglobal
When a user submits a file via an HTML form, PHP doesn't put the data in $_POST. Instead, it populates the $_FILES superglobal array.
To enable this, your HTML form must include the attribute enctype="multipart/form-data". Without this, the browser sends the filename as a text string rather than the actual file content.
HTMLstyle="color:#808080"><style="color:#4EC9B0">form action="/upload" method="POST" enctype="multipart/form-data"> style="color:#808080"><style="color:#4EC9B0">input type="file" name="avatar"> style="color:#808080"><style="color:#4EC9B0">button type="submit">Uploadstyle="color:#808080"></style="color:#4EC9B0">button> style="color:#808080"></style="color:#4EC9B0">form>
When submitted, $_FILES['avatar'] contains an associative array with five keys:
name: The original name of the file on the user's machine.type: The MIME type provided by the browser (e.g.,image/jpeg). Do not trust this.tmp_name: The path to the temporary file stored on the server.error: An integer representing the upload status (0 means success).size: The file size in bytes.
Securely Validating File Uploads
Never trust user input. A malicious user can easily spoof the type reported by the browser. To handle file uploads securely, you must perform server-side checks.
- Check for Upload Errors: Ensure
error === UPLOAD_ERR_OK. - Validate File Type: Use
finfo(File Information) to inspect the binary content of the file, not just the extension. - Limit Size: Prevent disk exhaustion by setting a maximum file size.
- Sanitize Filenames: Never use the user-provided
namedirectly. It could contain directory traversal characters like../.
Worked Example: Moving Uploaded Files
In our MVC project, we want to store user uploads in a storage/uploads directory. Here is how you process the request in a controller:
PHPpublic function storeAvatar() { $file = $_FILES['avatar']; #6A9955">// 1. Basic validation if ($file['error'] !== UPLOAD_ERR_OK) { throw new Exception("Upload failed with error code " . $file['error']); } #6A9955">// 2. Validate MIME type using finfo $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeType = $finfo->file($file['tmp_name']); $allowedTypes = ['image/jpeg', 'image/png']; if (!in_array($mimeType, $allowedTypes)) { throw new Exception("Invalid file type."); } #6A9955">// 3. Generate a secure, unique filename $extension = pathinfo($file['name'], PATHINFO_EXTENSION); $newFilename = bin2hex(random_bytes(16)) . '.' . $extension; $destination = __DIR__ . '/../../storage/uploads/' . $newFilename; #6A9955">// 4. Move to permanent storage if (move_uploaded_file($file['tmp_name'], $destination)) { echo "File saved successfully as " . $newFilename; } else { throw new Exception("Failed to move uploaded file."); } }
Hands-on Exercise
- Create a
storage/uploadsfolder in your project root and ensure it is writable by the web server. - Update your controller to handle a file upload.
- Test it by uploading a small image and verifying it appears in your
storagedirectory with a randomized name.
Common Pitfalls
- Trusting
$_FILES['type']: Always usefinfoto verify the actual content. A user can rename a malicious.phpscript to.jpgand trick the server if you only check extensions. - Overwriting files: Always generate a unique filename (e.g.,
random_bytesoruniqid) to prevent users from overwriting existing files. - Permissions: Ensure your
storage/uploadsdirectory is not directly executable by the web server. You can achieve this via.htaccessor by storing files outside thepublic/web root.
FAQ
Q: How do I handle large file uploads?
A: PHP has upload_max_filesize and post_max_size settings in php.ini. For very large files, consider using cloud storage services like S3, as discussed in Handling Large File Uploads: Streaming to S3 & Async Processing.
Q: Is move_uploaded_file safe?
A: Yes, it is designed specifically for this purpose. It verifies that the file being moved is indeed a valid uploaded file, preventing attackers from moving arbitrary files from your server's system directories.
Recap
Handling file uploads requires validating the file's contents, generating unique filenames to prevent collisions, and moving files from temporary storage to a secure location. By combining finfo for type checking and move_uploaded_file for transfer, you ensure your forms remain a secure entry point for user content.
Up next: We will explore performance optimization basics, including output buffering and query caching to make your application faster.
Work with me

WordPress Speed Optimization, Malware & Bug Fixes
Slow, hacked, or broken WordPress site? I clean it up, speed it up, and lock it down — fast, by a 12-year WordPress veteran.

Laravel SaaS MVP & Multi-Tenant App Development
Launch your SaaS MVP on Laravel — multi-tenant, subscription-ready, and built by the engineer behind a platform serving 10,000+ paying users.


