Question

Save an Image from a URL in PHP

phpimage

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:

  1. Request the URL.
  2. Receive the response body as binary data.
  3. 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-Type tells 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.

Take Quiz

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:

  1. $url stores the remote address to request.
  2. __DIR__ is the directory containing the current PHP script, so $path becomes a server-side file path.
  3. file_get_contents($url) sends a request and stores the returned image bytes in $data.
  4. If the request fails, PHP returns false, and the script stops.
  5. file_put_contents($path, $data) creates or replaces downloaded-flower.jpg with those bytes.
  6. The function returns the number of bytes written. A return value of false means 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.
Take Quiz

Real Codebase Usage

In real applications, developers usually avoid blindly downloading any URL supplied by a user. A safer download workflow is:

  1. Validate that the URL uses https or http.
  2. Use an allowlist of trusted hosts when possible.
  3. Set connection and total timeouts.
  4. Check for a successful HTTP status code such as 200.
  5. Verify that the response Content-Type starts with image/.
  6. Enforce a maximum file size.
  7. 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

ApproachBest forAdvantagesLimitations
file_get_contents() + file_put_contents()Small, simple downloadsShort and easy to understandNeeds allow_url_fopen; loads the entire file into memory
fopen() + stream_copy_to_stream()Larger downloadsStreams data instead of storing all bytes in memoryLess HTTP control than cURL
cURLProduction HTTP downloadsTimeouts, redirects, status codes, headers, authenticationRequires the cURL extension and more setup
readfile() with download headersSending an existing server file to a browserBrowser can download the fileDoes not itself save a remote URL to disk
DestinationMeaning
__DIR__ . '/flower.jpg'A file on the PHP server, beside the current script
/tmp/flower.jpgA temporary file on the PHP server (common on Linux)
Browser download responseA response that the visitor's browser may save locally
Take Quiz

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 .php URL 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more