PHP File Upload, Image Path and move_uploaded_file Debugger
Find whether your PHP upload problem is caused by the HTML form,
$_FILES, PHP limits, an upload error code, the temporary
file, destination directory, permissions, filename, MIME validation,
database path or browser image URL.
Use this tool when: $_FILES is empty,
move_uploaded_file() returns false, an uploaded file
never reaches the folder, a filename saves in MySQL but the image is
broken, uploads work in XAMPP but fail on hosting, or editing a record
accidentally removes the previous image.
Describe Your Upload Problem
Complete the fields you know. Optional code and error details improve the diagnosis.
Likely upload failure
$_FILES structure, upload error code, PHP
configuration, filesystem and database record.
Most Likely Root Causes
Fix in This Order
Form and $_FILES Checks
Path, Folder and Database Checks
Detected Upload Error
Stage-by-Stage Test Matrix
| Upload stage | Assessment | Exact test |
|---|
Personalized Secure Upload Templates
These examples are starting templates. Match the allowed file types, database connection, authorization rules, CSRF protection, storage folder and public URL to the real project.
Correct HTML upload form
Secure single-file upload with database save
Secure image upload validation
Multiple-file upload loop
Edit and safely replace an existing image
Security and Data-Safety Warnings
Upload Test Cases
Viva-Ready Explanation
Copyable Debugging Report
How a PHP File Upload Actually Works
A PHP upload moves through several separate stages. The browser first
builds a multipart POST request. The web server and PHP compare the
request with configured size and upload limits. PHP writes an accepted
upload to a temporary location and creates an entry in
$_FILES. Your application then validates the upload,
creates a safe destination filename, moves the temporary file and saves
a portable value in the database.
A visible “upload failed” message does not identify which stage failed. The form may never have sent a multipart request. PHP may have rejected the request before your script received it. The temporary file may exist while the destination directory does not. The move may succeed while the database insert fails. The database may contain the correct filename while the HTML uses a server path that a browser cannot open.
Stage 1: Browser form
The form must use POST and
enctype="multipart/form-data". The file input must
have a name that matches the PHP $_FILES key.
Stage 2: PHP request limits
PHP checks settings such as file_uploads,
upload_max_filesize, post_max_size and
the temporary upload directory.
Stage 3: Temporary upload
A successful request receives a temporary filename. Your script must process and move the file during the request.
Stage 4: Validation
Validate the upload error, size and file contents. Do not trust the original filename or browser-provided MIME type as final proof.
Stage 5: Permanent storage
Generate a collision-resistant filename and move the temporary file into a controlled directory outside executable code where possible.
Stage 6: Database and display
Save a portable filename or relative path. Build the browser URL separately from the server filesystem path.
What PHP Stores in $_FILES
A single file input named profile_image normally produces
an array with the original client filename, temporary server filename,
upload error code, reported size and browser-supplied type.
$_FILES["profile_image"]["name"]
$_FILES["profile_image"]["tmp_name"]
$_FILES["profile_image"]["error"]
$_FILES["profile_image"]["size"]
$_FILES["profile_image"]["type"]
Your code should inspect error before using
tmp_name. An error code other than
UPLOAD_ERR_OK means the application should stop and handle
that specific condition.
| Code | PHP constant | Meaning | First check |
|---|---|---|---|
| 0 | UPLOAD_ERR_OK |
PHP accepted the upload. | Continue with size, content and destination validation. |
| 1 | UPLOAD_ERR_INI_SIZE |
The file exceeds upload_max_filesize. |
Check the active PHP configuration and restart the relevant server after changes. |
| 2 | UPLOAD_ERR_FORM_SIZE |
The file exceeds the form’s MAX_FILE_SIZE value. |
Review both the form limit and server-side validation. |
| 3 | UPLOAD_ERR_PARTIAL |
The file was only partly uploaded. | Retry and investigate network, proxy or server interruption. |
| 4 | UPLOAD_ERR_NO_FILE |
No file was selected or received. | Check whether the field is optional and whether the user selected a file. |
| 6 | UPLOAD_ERR_NO_TMP_DIR |
PHP cannot use a temporary upload directory. | Check PHP and server temporary-directory configuration. |
| 7 | UPLOAD_ERR_CANT_WRITE |
PHP failed to write the upload to disk. | Check disk space, temporary storage and server permissions. |
| 8 | UPLOAD_ERR_EXTENSION |
A PHP extension stopped the upload. | Check hosting logs and installed upload/security extensions. |
Why $_FILES Can Be Completely Empty
A missing file key and a completely empty $_FILES array
are not always the same failure. A missing key can come from a form name
mismatch. A completely empty request may mean the browser did not send
multipart data or PHP rejected the entire request before normal form
processing.
Check these conditions in order:
- The form uses
method="post". - The form includes
enctype="multipart/form-data". - The file input has a valid
nameattribute. - The input is not disabled.
- The PHP key exactly matches the input name.
file_uploadsis enabled.- The entire request does not exceed
post_max_size.
When the total request exceeds post_max_size, the
application may receive empty $_POST and
$_FILES arrays. In that situation, checking only
$_FILES["field"]["error"] cannot help because the file
entry was never created.
Filesystem Path Versus Browser URL
PHP and the browser use two different address systems. PHP moves the file using a server filesystem path. HTML displays the file using a public URL.
Filesystem destination used by PHP
C:\xampp\htdocs\student-project\uploads\photo.jpg
/var/www/html/student-project/uploads/photo.jpg
Public URL used by the browser
http://localhost/student-project/uploads/photo.jpg
https://example.com/uploads/photo.jpg
A Windows path placed directly in an image src attribute
will not become a normal website URL. A portable application commonly
stores photo.jpg or
uploads/photo.jpg in the database, then builds the server
path and public URL separately.
$filesystemPath = __DIR__ . "/uploads/" . $filename;
$publicUrl = "uploads/" . rawurlencode($filename);
Why move_uploaded_file() Returns False
The function expects a temporary source that PHP recognizes as a file uploaded through HTTP POST. It also needs a valid destination path that the server can write.
Before moving, confirm:
- The upload error code equals
UPLOAD_ERR_OK. tmp_nameis not empty.is_uploaded_file($tmpName)returns true.- The destination directory exists.
- The destination directory is writable.
- The generated filename contains no path traversal.
- The destination path uses the correct project directory.
- The destination does not unintentionally overwrite another file.
Do not pass the original client filename directly into a destination path without controlling it. Generate the stored filename on the server and use only an approved extension.
Do Not Validate Uploads Using Only the Extension
A filename ending in .jpg does not prove that its contents
are a JPEG image. The original name and browser-reported type come from
the client and can be misleading.
For general files, Fileinfo can inspect the temporary file and return a
detected MIME type. For images, combine a MIME allowlist with an image
inspection function such as getimagesize(). Then map the
detected MIME type to an extension selected by the server.
$allowed = [
"image/jpeg" => "jpg",
"image/png" => "png",
"image/webp" => "webp"
];
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($tmpName);
if (!isset($allowed[$mime])) {
exit("Unsupported image type.");
}
$imageInfo = getimagesize($tmpName);
if ($imageInfo === false) {
exit("The uploaded file is not a readable image.");
}
Validation should reflect the project’s real needs. A profile-image feature should not accept arbitrary executable or archive files merely because the upload folder exists.
Use Server-Generated Filenames
Original filenames can contain spaces, unusual Unicode characters, repeated names or path-like content. Saving every image under a fixed name also causes each new upload to overwrite the previous one.
A collision-resistant filename can be generated like this:
$filename = bin2hex(random_bytes(16)) . "." . $extension;
Store the original filename separately only when the application needs to show it to the user. Do not use the original name as proof of type or as the permanent storage path.
Coordinate the File Move and Database Save
The filesystem and MySQL do not automatically share one transaction. This creates two possible inconsistencies:
- The database contains a filename but the file move failed.
- The file exists but the database insert or update failed.
A safer sequence is:
- Validate the request and upload error.
- Inspect file contents and size.
- Generate a safe destination filename.
- Move the file successfully.
- Save the confirmed filename inside a database transaction.
- If the database operation fails, remove the newly moved file.
When replacing an existing image, keep the old file until the new file has moved and the database update has committed. Delete the previous file only after the record points to the new file.
Multiple-File Uploads
A multiple-file input should use both the attribute and array-style name:
PHP then stores arrays under each file property. Your code must loop
through the matching indexes of name,
tmp_name, error and size.
The PHP max_file_uploads setting may also limit how many
files one request accepts. Application limits should be lower when the
project does not need large batches.
Upload Works in XAMPP but Fails on Hosting
A local Windows project and a hosted Linux server may behave differently even when the same PHP file is uploaded.
- Hosting upload and POST limits may be lower.
- The destination folder may not be writable by the web-server user.
- Linux filenames are usually case-sensitive.
- The document root and filesystem layout may be different.
open_basedirmay restrict accessible directories.- Fileinfo or image extensions may differ.
- Temporary storage may be full or unavailable.
- A hosting security rule may reject specific types or request sizes.
Use __DIR__ to anchor server paths to the current PHP
file rather than assuming the process always runs from the same working
directory.
Safe Upload Debugging Order
- Confirm the request uses POST and multipart encoding.
- Compare the input name with the exact
$_FILESkey. - Inspect whether
$_FILESis empty or contains an error code. - Compare the file size with both upload and POST limits.
- Check
tmp_nameandis_uploaded_file(). - Validate size and detected file contents.
- Confirm the destination directory exists and is writable.
- Generate a safe server-side filename.
- Move the file and check the return value.
- Save a portable database value only after the move succeeds.
- Build the HTML URL separately from the filesystem path.
- Test cleanup when the database operation fails.
Common Upload Fixes That Create Bigger Problems
Giving the folder unrestricted permissions
This may hide a permission issue while making the server less safe. Give only the web-server process the access it actually needs.
Trusting the browser MIME value
The client-provided type should not be the only content validation used by the server.
Saving original filenames directly
This creates collision, character, traversal and overwrite risks. Generate the storage name on the server.
Saving the database first
The database can contain a filename for a file that never reached permanent storage.
Using a server path in an image URL
PHP filesystem paths and browser URLs are not interchangeable.
Deleting the old image too early
If the new move or database update fails, the record may be left without either image.
Continue Your CodeZips Project Workflow
Technical References
Frequently Asked Questions
Why is $_FILES empty in PHP?
Check that the form uses POST, includes multipart/form-data, has a named and enabled file input, and does not exceed post_max_size. Confirm that file_uploads is enabled.
Why does move_uploaded_file() return false?
The source may not be a valid PHP upload, the upload may already have an error, the temporary filename may be empty, or the destination directory may be missing, unwritable or incorrectly constructed.
Why does the uploaded image show as broken?
The HTML may be using a filesystem path instead of a browser URL, the uploads folder may not be publicly reachable, the database path may already include the folder twice, or the filename may not match the actual stored file.
Should I store the complete upload path in MySQL?
A filename or portable relative path is usually easier to move between localhost and hosting. Build the filesystem destination and public browser URL separately in the application.
Is checking the file extension enough?
No. The server should inspect the temporary file’s contents and compare the detected type with an allowlist. Image uploads can also be checked as readable images.
Why does each upload replace the previous image?
The code may use a fixed destination name or reuse the same original filename. Generate a unique server-side filename for each upload.
Why does uploading work in XAMPP but fail on hosting?
Hosting may use different size limits, permissions, case-sensitive paths, temporary storage, PHP extensions, document roots or security restrictions.
How should I replace an existing profile image?
Validate and move the new image first, update the database successfully, and delete the previous file only after the record safely points to the new filename.
Does this tool upload or execute my file?
No. It analyzes the selected settings and pasted text locally in the browser. It does not send a file to your PHP server or execute the pasted code.
Final Takeaway
A reliable PHP upload must prove that the form submitted multipart data, PHP accepted the request, the temporary file is genuine, the contents and size are allowed, the destination is writable, the file moved successfully, the database saved the confirmed filename and the browser uses a public URL rather than a server path.

