W3docs

PHP File Upload

In today's digital age, file uploads are a common requirement in web development. Whether it's a profile picture, a document, or any other file type, the

File uploads are a common requirement in web development. Whether it's a profile picture, a PDF document, or a CSV import, letting users send files to your server is a core feature of most applications. In PHP this is handled by the move_uploaded_file() function together with the $_FILES superglobal array.

This chapter covers the full upload flow: configuring the HTML form, reading the uploaded file's metadata from $_FILES, validating it safely, and moving it to its final destination. It also walks through the upload error codes and the PHP configuration settings that control upload limits.

How a PHP file upload works

A file upload happens in three stages:

  1. The browser sends the file in a multipart/form-data POST request.
  2. PHP receives the file and writes it to a temporary location on disk, then exposes details about it in the $_FILES array.
  3. Your script validates the file and moves it from the temporary location to a permanent one with move_uploaded_file().

The temporary file is deleted automatically at the end of the request if you don't move it, so you must process it during the same request.

The $_FILES superglobal

When a file is uploaded, the information about it is stored in the $_FILES superglobal array. For a form field named userfile, the array contains these keys:

  • $_FILES['userfile']['name'] - The original name of the uploaded file.
  • $_FILES['userfile']['type'] - The MIME type of the uploaded file.
  • $_FILES['userfile']['size'] - The size of the uploaded file in bytes.
  • $_FILES['userfile']['tmp_name'] - The temporary location of the uploaded file on the server.
  • $_FILES['userfile']['error'] - An error code indicating if there was an issue during the file upload (see the error codes below).

Step 1: The HTML form

The form's enctype attribute must be set to multipart/form-data, and the method must be POST. Without multipart/form-data, the browser sends only the file name, not its contents, and $_FILES will be empty.

<form action="upload.php" method="POST" enctype="multipart/form-data">
  <input type="file" name="userfile">
  <input type="submit" value="Upload">
</form>

Step 2: Validate the upload

Never trust an uploaded file. Before moving it, check three things: that the upload itself succeeded, that the size is within your limit, and that the file is really the type you expect.

Check the error code

Always inspect $_FILES['userfile']['error'] first. PHP defines named constants for the possible values:

ConstantValueMeaning
UPLOAD_ERR_OK0No error, the file uploaded successfully.
UPLOAD_ERR_INI_SIZE1File exceeds upload_max_filesize in php.ini.
UPLOAD_ERR_FORM_SIZE2File exceeds the form's MAX_FILE_SIZE field.
UPLOAD_ERR_PARTIAL3The file was only partially uploaded.
UPLOAD_ERR_NO_FILE4No file was uploaded.
UPLOAD_ERR_NO_TMP_DIR6Missing a temporary folder.
UPLOAD_ERR_CANT_WRITE7Failed to write the file to disk.

Validate type and size securely

Do not rely on $_FILES['userfile']['type']. That value is supplied by the browser and is trivially spoofed by an attacker. Instead, detect the real MIME type from the file's contents with the finfo extension, and enforce a size limit yourself:

$file = $_FILES['userfile'];

// 1. Did the upload succeed?
if ($file['error'] !== UPLOAD_ERR_OK) {
  exit("Upload failed with error code " . $file['error']);
}

// 2. Enforce a maximum size (2 MB here).
$maxBytes = 2 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
  exit("File is too large.");
}

// 3. Detect the real MIME type, not the client-supplied one.
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime  = $finfo->file($file['tmp_name']);

$allowed = [
  'image/jpeg' => 'jpg',
  'image/png'  => 'png',
  'image/gif'  => 'gif',
];

if (!isset($allowed[$mime])) {
  exit("Only JPEG, PNG, and GIF images are allowed.");
}

Step 3: Move the file to its final destination

Once the file is validated, move it with move_uploaded_file(). It takes two arguments: the temporary path ($_FILES['userfile']['tmp_name']) and the target path. Using this function rather than copy() or rename() is important — it verifies the file was a genuine HTTP upload, which blocks an attacker from tricking your script into moving an arbitrary server file.

Generate the final file name yourself instead of trusting the original name. This avoids directory-traversal attacks (a name like ../../config.php) and overwriting existing files:

$targetDir = "uploads/";

// Build a safe, unique file name; never trust the client's name.
$extension  = $allowed[$mime];
$safeName   = bin2hex(random_bytes(8)) . "." . $extension;
$targetFile = $targetDir . $safeName;

if (move_uploaded_file($file['tmp_name'], $targetFile)) {
  echo "The file was uploaded as " . $safeName;
} else {
  echo "There was an error saving the file.";
}

For more detail on this function and its companion is_uploaded_file(), see the move_uploaded_file() and is_uploaded_file() reference chapters.

Configuration that affects uploads

A few php.ini directives silently cap what your script can receive. If uploads fail for large files even though your code is correct, check these:

  • file_uploads — must be On for uploads to work at all.
  • upload_max_filesize — the largest individual file PHP will accept (default 2M).
  • post_max_size — the largest total POST body; must be larger than upload_max_filesize.
  • max_file_uploads — the maximum number of files in a single request.

Conclusion

PHP file uploads are a fundamental part of web development. With the $_FILES superglobal array and the move_uploaded_file() function, you can handle uploads in just a few lines. The hard part is doing it safely: always check the upload error code, enforce a size limit, detect the real MIME type with finfo instead of trusting $_FILES[...]['type'], and generate your own file name so the client can never control where the file lands. For processing the saved file afterwards, see PHP file handling and form validation.

Practice

Practice
Which of the following statements regarding file uploads in PHP are correct?
Which of the following statements regarding file uploads in PHP are correct?
Was this page helpful?