How to Upload and Validate Files in PHP (Securely)

Uploading files, especially images, is a common need in PHP projects: profile pictures, product photos, document uploads. Doing it correctly means more than just moving a file; you have to validate it so users cannot upload dangerous or oversized files. This guide shows you how to build a secure file upload in PHP step by step, with the validation that turns a risky upload into a safe one.

File uploads are one of the most common places security goes wrong, because you are letting users put files on your server. The validation steps in this guide are not optional polish, they are what stop an upload feature from becoming a serious vulnerability.
What you’ll learn
  1. The HTML upload form
  2. How PHP receives an uploaded file
  3. The basic upload (and why it is not enough)
  4. Validating the file type
  5. Validating the file size
  6. Giving the file a safe, unique name
  7. The complete secure upload script

1. The HTML upload form

A file upload form needs two specific things: the method must be post, and it must include enctype="multipart/form-data", which is what allows files to be sent. Without that enctype, the file will not upload.

2. How PHP receives an uploaded file

When the form is submitted, PHP makes the file available in the $_FILES array. For the input named image, you get $_FILES['image'] with useful keys:

  • name, the original filename from the user
  • tmp_name, the temporary location where PHP stored the upload
  • size, the file size in bytes
  • type, the reported file type (do not fully trust this, more below)
  • error, an error code (0 means success)

3. The basic upload (and why it is not enough)

The core function is move_uploaded_file, which moves the file from its temporary location to a permanent folder:

// the bare minimum (NOT yet secure)
$target = "uploads/" . $_FILES['image']['name'];
move_uploaded_file($_FILES['image']['tmp_name'], $target);
Do not use this version in a real project. It accepts any file of any type and size, and trusts the user’s filename. That means someone could upload a malicious script instead of an image. The validation in the next steps is what makes uploading safe.

4. Validating the file type

You should only accept the file types you actually want (here, images). The reliable way is to check the real extension and, better, the actual file content, not just the browser-reported type which can be faked.

// allow only specific image extensions
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));

if (!in_array($ext, $allowed)) {
  die("Only JPG, PNG, and GIF files are allowed.");
}

// stronger: verify it is really an image
$check = getimagesize($_FILES['image']['tmp_name']);
if ($check === false) {
  die("That file is not a valid image.");
}

Using getimagesize is a strong check because it confirms the file is genuinely an image, not just something renamed to end in .jpg.

5. Validating the file size

Always cap the file size so users cannot fill your server with huge files. The size is in bytes, so convert from megabytes for readability:

// limit to 2 MB
$maxSize = 2 * 1024 * 1024; // 2 MB in bytes
if ($_FILES['image']['size'] > $maxSize) {
  die("File is too large. Maximum size is 2 MB.");
}

6. Giving the file a safe, unique name

Never save the file under the user’s original name. Two users could upload “photo.jpg” and overwrite each other, and original names can contain unsafe characters. Generate a unique name instead:

// build a safe, unique filename
$newName = uniqid('img_', true) . '.' . $ext;
$target = "uploads/" . $newName;

uniqid generates a unique value, so every uploaded file gets its own name and nothing is overwritten.

7. The complete secure upload script

Putting all the validation together, here is a complete, safe upload script:

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {

  $file = $_FILES['image'];

  // 1. check for upload errors
  if ($file['error'] !== 0) {
    die("Upload failed. Please try again.");
  }

  // 2. validate type
  $allowed = ['jpg', 'jpeg', 'png', 'gif'];
  $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
  if (!in_array($ext, $allowed) || getimagesize($file['tmp_name']) === false) {
    die("Only valid image files are allowed.");
  }

  // 3. validate size (2 MB)
  if ($file['size'] > 2 * 1024 * 1024) {
    die("File is too large. Maximum size is 2 MB.");
  }

  // 4. unique name + move
  $newName = uniqid('img_', true) . '.' . $ext;
  if (move_uploaded_file($file['tmp_name'], "uploads/" . $newName)) {
    echo "Uploaded successfully as " . $newName;
    // usually you now save $newName in the database
  } else {
    echo "Could not save the file.";
  }
}
In a real project you store the generated filename (not the file itself) in your database, then display the image later by pointing to uploads/THE_SAVED_NAME. This is exactly how product and profile images work in the projects on CodeZips, browse the ready-to-run PHP projects with full source code to see image upload used inside a complete application.

Frequently asked questions

Why is my PHP file upload not working?

The most common reasons are a missing enctype="multipart/form-data" on the form, the upload folder not existing or not being writable, or the file exceeding PHP’s upload limits (upload_max_filesize and post_max_size in php.ini). Check those first.

How do I store the uploaded file in the database?

You do not store the file itself; you store its filename (or path) as text in a column, then save the actual file in a folder. To show it later, output an image tag pointing to that folder plus the saved filename.

Why give uploaded files a unique name?

To prevent two files with the same name from overwriting each other, and to avoid unsafe characters in user-supplied filenames. Generating a unique name with uniqid solves both problems.

How do I validate that an upload is really an image?

Check the file extension against an allowed list, and use getimagesize, which returns false if the file is not a real image. Together these stop someone from uploading a non-image file renamed to look like one.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top