Question
How can I use PHP to download an image served at a URL such as http://example.com/image.php and save it under a new filename? The URL returns only a single image (for example, a flower image), even though its path ends in .php.
Short Answer
You will learn that PHP can download image bytes from a URL regardless of the URL's extension, then write those bytes to a file with any suitable name. You will also learn when the file is saved on the server versus downloaded to a visitor's computer, and how to add basic error handling.
Concept
A URL does not have to end in .jpg or .png to return an image. image.php can run PHP code and send an HTTP response whose body contains JPEG, PNG, WebP, or another image format.
To save that image, PHP must:
- Request the URL.
- Receive the response body as binary data.
- Write that binary data to a local file.
The file's extension is only a naming convention. It should match the image's actual format so image programs can recognize it easily. For example, data returned with Content-Type: image/jpeg should normally be saved as flower.jpg.
A key distinction: PHP normally runs on a web server. Therefore, file_put_contents() saves to the server's disk, not automatically to the computer of someone viewing the page. To save a file to the visitor's computer, PHP sends download headers and streams the content in its HTTP response; the browser handles the actual download location.
Mental Model
Think of the URL as a tap that delivers a stream of water, and the destination file as an empty bottle.
- The URL path (
image.php) is the label on the tap, not proof of what flows out. - The HTTP
Content-Typetells you what kind of content is flowing out. - PHP collects the image data and pours it into a new bottle named
flower.jpg.
If PHP runs on a remote hosting server, the bottle is placed in that server's cupboard. If you want the user's computer to receive it, PHP must hand the stream back to the user's browser as a download.
Syntax and Examples
For a small, trusted image URL, PHP streams provide a concise solution:
<?php
$sourceUrl = 'https://example.com/image.php';
$destination = __DIR__ . '/flower.jpg';
$imageData = file_get_contents($sourceUrl);
if ($imageData === false) {
throw new RuntimeException('Could not download the image.');
}
if (file_put_contents($destination, $imageData) === false) {
throw new RuntimeException('Could not save the image.');
}
echo "Saved image to: $destination";
file_get_contents() makes the HTTP request and returns the response body. Since image data is binary, do not modify it or treat it as text. file_put_contents() writes the exact bytes to the destination file.
This requires allow_url_fopen to be enabled in PHP configuration. For production code, especially when redirects, timeouts, or HTTP status checks matter, cURL is usually the better choice:
<?php
$sourceUrl = 'https://example.com/image.php';
$destination = __DIR__ . '/flower.jpg';
$input = fopen($sourceUrl, 'rb');
$output = fopen($destination, 'wb');
if ($input === false || $output === false) {
throw new RuntimeException('Could not open source or destination.');
}
stream_copy_to_stream($input, $output);
fclose($input);
fclose($output);
Opening files with rb and wb means read-binary and write-binary. Streaming avoids keeping the entire image in memory.
Step by Step Execution
Consider this code:
<?php
$url = 'https://example.com/image.php';
$path = __DIR__ . '/downloaded-flower.jpg';
$data = file_get_contents($url);
if ($data === false) {
exit('Download failed.');
}
$bytesWritten = file_put_contents($path, $data);
if ($bytesWritten === false) {
exit('Saving failed.');
}
echo "Wrote $bytesWritten bytes.";
Step by step:
$urlstores the remote address to request.__DIR__is the directory containing the current PHP script, so$pathbecomes a server-side file path.file_get_contents($url)sends a request and stores the returned image bytes in$data.- If the request fails, PHP returns
false, and the script stops. file_put_contents($path, $data)creates or replacesdownloaded-flower.jpgwith those bytes.- The function returns the number of bytes written. A return value of
falsemeans writing failed, commonly because of permissions or an invalid directory.
After a successful run, the image is in the same directory as the script, assuming that directory is writable.
Real World Use Cases
- Importing product images: An admin tool downloads supplier image URLs and stores copies locally or in object storage.
- Creating cached thumbnails: A service fetches a remote avatar once, saves it, and avoids repeatedly requesting the original server.
- Generating reports: A PHP script downloads charts or map snapshots and attaches them to generated documents.
- Archiving assets: A scheduled task copies images from an external API to a controlled backup location.
- Proxying a download: An authenticated application fetches a private remote image and returns it to an authorized browser without exposing the upstream URL.
Real Codebase Usage
In real applications, developers usually avoid blindly downloading any URL supplied by a user. A safer download workflow is:
- Validate that the URL uses
httpsorhttp. - Use an allowlist of trusted hosts when possible.
- Set connection and total timeouts.
- Check for a successful HTTP status code such as
200. - Verify that the response
Content-Typestarts withimage/. - Enforce a maximum file size.
- Generate the local filename yourself rather than using an untrusted filename.
A cURL-based helper gives more control:
<?php
function downloadImage(string $url, string $destination): void
{
$file = fopen($destination, 'wb');
if ($file === false) {
throw new RuntimeException('Unable to create destination file.');
}
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_FILE => $file,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 30,
CURLOPT_FAILONERROR => true,
CURLOPT_USERAGENT => 'MyImageDownloader/1.0',
]);
$success = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
fclose($file);
if ($success === false) {
@unlink($destination); // Remove a possibly incomplete file.
throw new RuntimeException("Image download failed: $error");
}
}
// downloadImage('https://example.com/image.php', __DIR__ . '/flower.jpg');
For public-facing code, protect against server-side request forgery (SSRF): do not let untrusted users make your server request internal addresses such as http://localhost/ or private network IP addresses.
Common Mistakes
Assuming .php means the response is PHP source code
The extension is part of the route. A PHP script can return image bytes:
header('Content-Type: image/jpeg');
readfile('flower.jpg');
Save the response based on its actual content type, not its URL suffix.
Saving on the wrong computer
This saves on the server:
file_put_contents('/var/www/app/flower.jpg', $data);
It cannot silently choose a folder on a website visitor's PC. Browsers intentionally prevent that for security. Send a download response if the visitor should receive the file.
Ignoring failures
This can create confusion when the request fails:
file_put_contents('flower.jpg', file_get_contents($url));
Check both operations because a network error, disabled URL wrappers, permissions issue, or missing directory can fail.
Saving an error page as an image
A URL may return an HTML login page or a 404 page rather than an image. The saved file may exist but be invalid. Check the HTTP status and Content-Type, particularly with cURL.
Using text functions on binary data
Do not use functions that alter encoding or line endings on image bytes. Copy or write the bytes unchanged.
Using a user-provided filename directly
Avoid this:
$destination = __DIR__ . '/uploads/' . $_GET['filename'];
A value such as ../../important-file can escape the intended folder. Generate a safe filename, for example with bin2hex(random_bytes(16)) . '.jpg'.
Comparisons
| Approach | Best for | Advantages | Limitations |
|---|---|---|---|
file_get_contents() + file_put_contents() | Small, simple downloads | Short and easy to understand | Needs allow_url_fopen; loads the entire file into memory |
fopen() + stream_copy_to_stream() | Larger downloads | Streams data instead of storing all bytes in memory | Less HTTP control than cURL |
| cURL | Production HTTP downloads | Timeouts, redirects, status codes, headers, authentication | Requires the cURL extension and more setup |
readfile() with download headers | Sending an existing server file to a browser | Browser can download the file | Does not itself save a remote URL to disk |
| Destination | Meaning |
|---|---|
__DIR__ . '/flower.jpg' | A file on the PHP server, beside the current script |
/tmp/flower.jpg | A temporary file on the PHP server (common on Linux) |
| Browser download response | A response that the visitor's browser may save locally |
Cheat Sheet
// Small remote image download
$data = file_get_contents($url);
if ($data === false) {
throw new RuntimeException('Download failed');
}
$written = file_put_contents(__DIR__ . '/flower.jpg', $data);
if ($written === false) {
throw new RuntimeException('Write failed');
}
// Stream source to destination: better for larger files
$source = fopen($url, 'rb');
$target = fopen(__DIR__ . '/flower.jpg', 'wb');
stream_copy_to_stream($source, $target);
fclose($source);
fclose($target);
- A
.phpURL can return an image. - Use a matching extension when you know the format.
file_put_contents()saves where PHP runs: usually the server.- Ensure the destination directory exists and is writable.
- Use cURL for redirects, HTTP status checks, timeouts, or authenticated requests.
- Never fetch arbitrary user URLs without SSRF protections.
FAQ
Can PHP save an image from a URL that ends in .php?
Yes. PHP downloads the HTTP response body. The URL extension does not determine whether the response is an image.
Where does PHP save the downloaded image?
It saves on the filesystem of the machine running PHP. On a hosted website, that is normally your web server, not the visitor's computer.
Why does file_get_contents($url) return false?
The URL may be unreachable, SSL may fail, allow_url_fopen may be disabled, or the server may reject the request. Use error handling and consider cURL for detailed diagnostics.
How do I save the file on the user's computer instead?
Send the image as an HTTP download response with Content-Disposition: attachment. The browser then prompts or follows its download settings; PHP cannot choose a local folder without user/browser involvement.
Should I use .jpg or .png as the new filename?
Use the extension matching the actual response format. Check the server's Content-Type header or inspect the downloaded image if the source format is not guaranteed.
Can I download a large image with file_get_contents()?
You can, but it loads the complete file into PHP memory. Use streams or cURL writing directly to a file for larger downloads.
Is it safe to download a URL submitted by a user?
Not by default. Restrict hosts, validate URLs, block private/internal addresses, enforce timeouts and size limits, and validate that the response is really an image.
Mini Project
Description
Build a small command-line PHP image archiver. It downloads a known image URL, stores it in a local downloads folder using a timestamped name, and reports clear errors if the request or file write fails. This mirrors the first stage of many asset-import jobs.
Goal
Download an image from a configured URL and save it as a uniquely named local file.
Requirements
Create a downloads directory if it does not exist.
Use a configured HTTPS image URL.
Save the image with a new timestamped filename.
Do not overwrite an existing image.
Show the final saved path or a useful error message.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.