PHP File Upload
Handling uploaded files is one of the most security-sensitive jobs in PHP. Get the basics right and most footguns go away.
The HTML side
HTML
<form method="post" enctype="multipart/form-data">
<input type="file" name="upload">
<button>Upload</button>
</form>
Without enctype="multipart/form-data" the file never gets uploaded.
The PHP side
PHP
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$f = $_FILES['upload'] ?? null;
if (!$f || $f['error'] !== UPLOAD_ERR_OK) {
die('Upload failed.');
}
// Validate size
if ($f['size'] > 2 * 1024 * 1024) die('Max 2 MB.');
// Validate MIME — using fileinfo, not the client-supplied $f['type']
$mime = mime_content_type($f['tmp_name']);
if (!in_array($mime, ['image/jpeg', 'image/png'], true)) {
die('JPEG or PNG only.');
}
// Pick a safe destination filename
$ext = pathinfo($f['name'], PATHINFO_EXTENSION);
$name = bin2hex(random_bytes(8)) . '.' . strtolower($ext);
$dest = __DIR__ . '/uploads/' . $name;
move_uploaded_file($f['tmp_name'], $dest);
echo "Saved as $name";
}
Why each check matters
| Check | Threat |
|---|---|
Verify UPLOAD_ERR_OK | PHP-reported partial/over-size uploads. |
| Cap file size | Disk-fill DoS. |
| Detect MIME server-side | Browser-supplied type is forgeable. |
| Generate new filename | Path traversal, filename injection. |
| Store outside docroot, or block PHP execution | Uploading a .php and running it. |
Use move_uploaded_file (not rename) | Sanity check it came from an upload. |
Size limits
PHP enforces these in php.ini — the smallest wins:
upload_max_filesizepost_max_sizememory_limit
Tip: For files larger than a few MB, consider direct-to-S3 uploads with a pre-signed URL. PHP never touches the bytes — your server stays unloaded.
Example
Example
<?php
// HTML side:
// <form method="post" enctype="multipart/form-data">
// <input type="file" name="upload">
// <button>Upload</button>
// </form>
if (isset($_FILES['upload'])) {
move_uploaded_file($_FILES['upload']['tmp_name'], 'uploads/' . basename($_FILES['upload']['name']));
}
Try it Yourself »
Exercise
Move an uploaded file to its final location with…
($_FILES['upload']['tmp_name'], $dest);
Snake_case; 18 chars.
Discussion
Loading…