$_FILES

Yo Yogesh.M Updated 29 Oct 2026
7 min read

What Is $_FILES?

In simple terms: $_FILES contains information about files a user uploads through a form — the original file name, its size, where PHP temporarily stored it, and whether the upload succeeded. Technically: $_FILES is a superglobal populated when a POST request is sent with Content-Type: multipart/form-data. For each file input, PHP saves the uploaded file to a temporary location and describes it in $_FILES. Unless your script moves the file somewhere permanent, PHP deletes it automatically when the request ends.

Why Uploads Need Special Care

File uploads are among the most security-sensitive features of a web application. An attacker who can upload a PHP script into a public folder and then request it can run any code on your server. Every upload must therefore be validated and stored safely — this lesson covers both.


The Upload Form

<form method="post" action="/upload.php" enctype="multipart/form-data">
    <input type="file" name="avatar" accept="image/png, image/jpeg">
    <button type="submit">Upload</button>
</form>

Three things are required:

  1. method="post" — files cannot be sent with GET.
  2. enctype="multipart/form-data" — without it, the browser sends only the file name, and $_FILES stays empty.
  3. A name attribute on the file input — it becomes the key in $_FILES. The accept attribute only filters the browser's file picker; it provides no security, because requests can be sent without the browser.

The Structure of $_FILES

After a user uploads profile photo.jpg, $_FILES looks like this:

[
    'avatar' => [
        'name'      => 'profile photo.jpg',      // original name from the client
        'full_path' => 'profile photo.jpg',      // path within an uploaded folder (PHP 8.1+)
        'type'      => 'image/jpeg',             // MIME type claimed by the client
        'tmp_name'  => '/tmp/phpA1b2C3',         // temporary file on the server
        'error'     => 0,                         // UPLOAD_ERR_OK
        'size'      => 52341,                     // size in bytes
    ],
]
KeyMeaningTrustworthy?
nameOriginal file nameNo — chosen by the client
full_pathRelative path when a directory is uploaded (PHP 8.1+)No
typeMIME type sent by the browserNo — easily faked
tmp_namePath of the temporary file on the serverYes (set by PHP)
errorUpload status codeYes
sizeSize in bytesYes (measured by PHP)

Only tmp_name, error, and size are determined by PHP. Never use name or type for security decisions.


Upload Error Codes

Always check error first:

ConstantValueMeaning
UPLOAD_ERR_OK0Upload succeeded
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_PARTIAL3File was only partially uploaded
UPLOAD_ERR_NO_FILE4No file was selected
UPLOAD_ERR_NO_TMP_DIR6Server has no temporary folder
UPLOAD_ERR_CANT_WRITE7Server failed to write the file
UPLOAD_ERR_EXTENSION8A PHP extension stopped the upload

Turning Codes Into Messages

<?php
function uploadErrorMessage(int $code): ?string
{
    return match ($code) {
        UPLOAD_ERR_OK => null,
        UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'The file is too large.',
        UPLOAD_ERR_PARTIAL => 'The upload was interrupted. Please try again.',
        UPLOAD_ERR_NO_FILE => 'Please choose a file.',
        default => 'The server could not save the file. Please try later.',
    };
}
foreach ([0, 1, 4, 7] as $code) {
    var_dump(uploadErrorMessage($code));
}

Output

NULL
string(22) "The file is too large."
string(21) "Please choose a file."
string(53) "The server could not save the file. Please try later."

Server-side problems (codes 6, 7, 8) should also be logged, because they indicate a configuration issue rather than a user mistake.


Validating and Saving an Upload

Step by Step

  1. Check the error code.
  2. Check the size against your own limit.
  3. Detect the real file type from the file contents with finfo — not from type or the extension.
  4. Generate a new file name on the server; never reuse the client's name.
  5. Move the file with move_uploaded_file(), which also verifies that the file really came from an upload.

Example: Avatar Upload

<?php
// upload.php
function saveAvatar(array $file, string $targetDir): string
{
    if (!isset($file['error']) || is_array($file['error'])) {
        throw new RuntimeException('Invalid upload.');
    }
    if ($file['error'] !== UPLOAD_ERR_OK) {
        throw new RuntimeException(uploadErrorMessage($file['error']));
    }
    $maxBytes = 2 * 1024 * 1024; // 2 MB
    if ($file['size'] > $maxBytes) {
        throw new RuntimeException('The file must be 2 MB or smaller.');
    }
    $allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
    $mime = (new finfo(FILEINFO_MIME_TYPE))->file($file['tmp_name']);
    if (!isset($allowed[$mime])) {
        throw new RuntimeException('Only JPEG, PNG, and WebP images are allowed.');
    }
    $fileName = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
    $destination = rtrim($targetDir, '/') . '/' . $fileName;
    if (!move_uploaded_file($file['tmp_name'], $destination)) {
        throw new RuntimeException('The server could not save the file.');
    }
    return $fileName;
}
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    try {
        $saved = saveAvatar($_FILES['avatar'] ?? [], __DIR__ . '/../storage/avatars');
        echo 'Uploaded as ' . htmlspecialchars($saved);
    } catch (RuntimeException $e) {
        http_response_code(422);
        echo htmlspecialchars($e->getMessage());
    }
}

Output After Uploading a Valid JPEG (the name is random each time)

Uploaded as 3f9a1c0e8b7d4a2e9c6b1f0a5d8e7c42.jpg

Why Each Step Matters

StepProtects Against
is_array($file['error']) checkRequests that send several files where one was expected (avatar[])
Error code checkProcessing incomplete or missing files
Size limitDisk exhaustion and oversized images
finfo MIME detectionFiles with a fake extension or fake type, such as shell.php renamed to photo.jpg
Extension from your allow-listSaving a file with an executable extension like .php
Random file nameOverwriting other files, path traversal (../../index.php), and guessing file names
move_uploaded_file()Moving files that did not come from an upload
Storage outside the web root (storage/)Direct execution of uploaded files through a URL

finfo requires the fileinfo extension, which is enabled by default. For images, re-encoding them (for example with the GD or Imagick extension) removes hidden content and metadata, adding another layer of safety.

Serving Stored Files

Files stored outside the web root are delivered through a PHP script that checks permissions and sends the file:

<?php
// avatar.php?file=3f9a1c0e8b7d4a2e9c6b1f0a5d8e7c42.jpg
$file = basename($_GET['file'] ?? '');
$path = __DIR__ . '/../storage/avatars/' . $file;
if (!preg_match('/^[a-f0-9]{32}\.(jpg|png|webp)$/', $file) || !is_file($path)) {
    http_response_code(404);
    exit;
}
header('Content-Type: ' . (new finfo(FILEINFO_MIME_TYPE))->file($path));
header('X-Content-Type-Options: nosniff');
readfile($path);

The strict pattern accepts only file names in the format your upload code creates, which prevents path traversal. If uploads must be stored inside the public folder, configure the web server to never execute scripts in that folder.


Multiple Files

With name="photos[]" and the multiple attribute, users can select several files:

<input type="file" name="photos[]" multiple>

PHP organizes the result by property, not by file:

$_FILES['photos'] = [
    'name'     => ['beach.jpg', 'sunset.png'],
    'type'     => ['image/jpeg', 'image/png'],
    'tmp_name' => ['/tmp/phpA1', '/tmp/phpB2'],
    'error'    => [0, 0],
    'size'     => [52341, 81022],
];

A helper converts this into one array per file:

<?php
function normalizeFiles(array $files): array
{
    if (!is_array($files['name'] ?? null)) {
        return [$files];
    }
    $normalized = [];
    foreach (array_keys($files['name']) as $index) {
        foreach ($files as $property => $values) {
            $normalized[$index][$property] = $values[$index];
        }
    }
    return $normalized;
}
$example = [
    'name' => ['beach.jpg', 'sunset.png'],
    'type' => ['image/jpeg', 'image/png'],
    'tmp_name' => ['/tmp/phpA1', '/tmp/phpB2'],
    'error' => [0, 0],
    'size' => [52341, 81022],
];
foreach (normalizeFiles($example) as $file) {
    echo "{$file['name']}: {$file['size']} bytes, error {$file['error']}\n";
}

Output

beach.jpg: 52341 bytes, error 0
sunset.png: 81022 bytes, error 0

Each normalized file can then be passed to the same validation function used for single uploads. Limit the number of files you accept; PHP's max_file_uploads setting (default 20) is an upper bound.


Configuration Limits

php.ini SettingDefaultMeaning
file_uploadsOnEnables uploads
upload_max_filesize2MMaximum size per file
post_max_size8MMaximum size of the whole request; must be larger than upload_max_filesize
max_file_uploads20Maximum number of files per request
upload_tmp_dirSystem defaultTemporary folder

If the whole request exceeds post_max_size, both $_FILES and $_POST are empty — the error codes above are never set. Check for this case explicitly, for example when REQUEST_METHOD is POST but $_FILES is empty. Web servers have their own limits too (such as Nginx's client_max_body_size).


Common Mistakes

Forgetting enctype="multipart/form-data"

Effect: $_FILES is empty. Fix: Add the attribute to the form.

Trusting name, type, or the Extension

Effect: Attackers upload executable scripts disguised as images. Fix: Detect the MIME type with finfo and choose the extension from an allow-list.

Storing Uploads in a Public, Executable Folder

Effect: Uploaded scripts can be executed through a URL. Fix: Store outside the web root, or disable script execution in the upload folder.

Keeping the Original File Name

Effect: Overwritten files, path traversal, and leaked personal information in names. Fix: Generate random names; store the original name in the database if you need to display it.

Using copy() or rename() Instead of move_uploaded_file()

Effect: No check that the file came from an upload. Fix: Always use move_uploaded_file().


Best Practices

  • Check the error code, size, and real MIME type of every upload.
  • Generate server-side file names and use extensions from an allow-list.
  • Store uploads outside the web root and serve them through a controlled script.
  • Normalize multi-file uploads and validate each file individually.
  • Align upload_max_filesize, post_max_size, and web server limits with your application's needs. Official reference: https://www.php.net/manual/en/reserved.variables.files.php

0 Comments

Reviewed before they appear

No comments yet.

Data Mining
Introduction to Data Mining Data Mining Implementation Process Data Mining Architecture Data Mining Techniques KDD: Knowledge Discovery in Databases History of Data Mining Data Mining Tools Data Mining vs Machine Learning Facebook Data Mining Social Media Data Mining Methods Clustering in Data Mining Text Data Mining Bagging vs Boosting Data Mining vs Data Warehousing Social Media Data Mining Data Mining: Bayesian Classifiers Data Mining: World Wide Web Different Types of Clustering Bitcoin Mining and Blockchain Data Mining Orange Data Mining Data Mining vs Big Data Decision Tree Induction Educational Data Mining Data Mining in Healthcare Apriori Algorithm Data Integration in Data Mining Difference Between Data Mining and Text Mining Discretization in Data Mining Redundancy and Correlation in Data Mining Classification of Data Mining Systems Classification Algorithms in Data Mining Data Generalization in Data Mining Classification and Prediction in Data Mining Regression in Data Mining What Is an Outlier in Data Mining Data Cleaning in Data Mining Data Processing in Data Mining Data Mining Task Primitives Web Content Mining, Web Structure Mining, and Web Usage Mini Binning in Data Mining KDD vs. Data Mining Noise in Data Mining BIRCH in Data Mining Stemming in Data Mining Feature Transformation in Data Mining Visual and Audio Data Mining Class Comparison Methods in Data Mining Data Selection in Data Mining Tasks and Functionalities of Data Mining Ubiquitous and Invisible Data Mining Importance of Data Mining Data Quality in Data Mining Mining Frequent Patterns in Data Mining Data Warehouse and OLAP Technology for Data Mining Need for Data Mining Pattern Evaluation Methods in Data Mining Logarithms and Exponents in Complexity Analysis Time Series Clustering What is the C4.5 Algorithm and How Does It Work? Expanding Contractions in Text Mining Cosine Similarity in Data Mining Lazy Learning in Data Mining Neural Networks in Data Mining Building a Text Normalizer Shallow Parsing Data Pipeline Orchestration Big Data vs Small Data Myths of Data Mining Data Reduction in Data Mining Data Mining Models Difference Between Data Mining and Data Science Difference Between Data Mining and Statistics Difference Between Data Mining and Web Mining Difference Between Data Mining and Data Analysis Difference Between Business Intelligence and Data Mining Difference Between Data Mining and Data Visualization Data Mining vs Data Analytics Types of Data Mining Difference Between Data Profiling and Data Mining Predictive Analytics vs Data Mining Difference Between Data Mining and OLAP Descriptive and Predictive Data Mining Difference Between Spatial and Temporal Data Mining Difference Between Classification and Clustering in Data Min Hierarchical Clustering in Data Mining Density-Based Clustering in Data Mining What Is Aggregation in Data Mining?
Ask about this post
AI Ask about this post

Ask questions about $_FILES and get answers drawn from it.

Signed-in readers only.