How could I cache images using Laravel

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering Image Caching in Laravel: Beyond Simple URL Storage

Dealing with external APIs, especially those providing dynamic content like Google Places photos, introduces a classic performance challenge: how do we avoid redundant API calls and ensure fast loading times? When you start dealing with binary assets like images, simply caching the resulting URL often falls short. As senior developers working with frameworks like Laravel, we need solutions that handle the actual data efficiently, not just the pointers to it.

This post dives deep into how you can effectively cache images retrieved from external sources within your Laravel application, moving beyond basic URL caching to achieve true performance optimization.

The Limitation of Basic Caching

You've correctly identified the limitation: using Cache::get($id . 'photos') only stores the generated public URL. While this prevents hitting the Google Places API repeatedly for that specific request, it doesn't solve the core problem if you intend to serve these images directly from your application or cache them for long-term delivery. If the image needs to be served quickly and securely, caching the link is a temporary fix.

To truly cache an image, you need to cache the binary data itself and manage its storage lifecycle within your system. This requires a multi-step approach involving downloading, storing, and managing file paths.

Solution 1: Storing Images Using Laravel Storage

The most robust solution is to treat the retrieved photo reference as a trigger to download the actual image and store it securely on your server using Laravel's built-in File Storage system. This ensures that subsequent requests for the same location do not involve external API calls; they simply retrieve the file from your local storage.

Step-by-Step Implementation

  1. Check the Cache: Before attempting any download, check if the image exists in your storage.
  2. Download (If Necessary): If it doesn't exist, make the external API call to get the photo reference, construct the URL, download the actual image content, and save it to your local disk (e.g., public).
  3. Cache the Result: Store the resulting file path or the image stream in your cache for fast retrieval on subsequent requests.

Here is a conceptual example demonstrating how you might structure this logic within a service class:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;

class PhotoService
{
    protected $disk = 'public'; // Assuming 'public' disk is configured in config/filesystems.php

    public function getCachedPhoto(string $photoReference, string $locationId)
    {
        $cacheKey = "photos:{$locationId}:{$photoReference}";

        // 1. Check if the image is already cached
        if (Cache::has($cacheKey)) {
            return Storage::disk($this->disk)->url($cacheKey);
        }

        // 2. Image not found, proceed with external fetch and storage
        $googleApiUrl = "https://maps.googleapis.com/maps/api/place/photo?photoreference={$photoReference}&sensor=false&maxheight=400&maxwidth=400&key={$this->apiKey}";

        // In a real application, you would use Guzzle here to fetch the image stream
        $response = Http::get($googleApiUrl); 

        if ($response->successful()) {
            $imageData = $response->body();
            $filename = "location_{$locationId}_{$photoReference}.jpg";
            
            // 3. Store the actual binary data locally
            Storage::disk($this->disk)->put($filename, $imageData);

            // 4. Cache the resulting URL for fast retrieval later
            Cache::put($cacheKey, Storage::disk($this->disk)->url($filename), now()->addDays(30)); // Cache for 30 days
            
            return Storage::disk($this->disk)->url($filename);
        }

        // Handle error if API call failed
        throw new \Exception("Failed to retrieve photo data.");
    }
}

Solution 2: Optimizing Delivery with CDNs

While local storage solves the caching problem on your server, serving large image files directly from your application can still lead to latency and bandwidth issues. For high-traffic applications, the next logical step is integrating a Content Delivery Network (CDN), such as Cloudflare or AWS CloudFront.

By storing the cached images in your Laravel Storage and then using a CDN to proxy requests, you distribute the load globally and leverage edge caching mechanisms provided by the CDN itself. This strategy aligns perfectly with modern, scalable architectural patterns promoted by Laravel Company principles, focusing on clean separation of concerns.

Conclusion

Caching external image data requires moving beyond simple key-value storage. By leveraging Laravel's Storage facade alongside robust caching mechanisms like Redis or Memcached, you can transform a slow, repetitive API call into an efficient, cached file retrieval process. Start by implementing local file storage for the binary assets, and then layer on a CDN for world-class delivery performance. This approach ensures your application remains fast, scalable, and adheres to best practices in modern web development.