Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

liamseys's avatar

Serving public images from local / aws S3

Hello

I have a job downloading images from an external API. On my local machine I download the images to my local storage (public folder as the images should be accessible to the public). From my production server I save all images to my S3 bucket. I use the following code to store the file:

Storage::put('public/images/' . $fileName, $contents, 'public');

I have the following mutator in my model to serve the link to the image:

return asset('images/' . $this->images()->first()->file_name);

This works locally as I have a symlink, but on the production it isn't working as the files get saved in the public folder in the bucket. Whats the best way to solve this? Thanks!

0 likes
5 replies
martinbean's avatar

Whats the best way to solve this?

@liamseys The “best way” to solve your problem is to just use Laravel’s filesystem component as intended.

The Storage façade (and underlying filesystem component) is meant to give you disk-agnostic access to files. It means you should be able to use the same code regardless of what disk driver you’re using, be it local, S3, or anything else.

It’s unclear whether these are user-uploaded images or just generic application images. If they’re user-uploaded images, then you shouldn’t be uploading them to your /public folder. This folder should be version-controlled and for application images only. If users are randomly dropping files in this folder, then your website is going to be out of sync with your source repository (Git or whatever).

If these are user-uploaded images, then store them in your /storage folder and serve appropriately-sized thumbnails. You don’t want to be serving 4 MB images just because that’s what a user uploaded.

So, back to the problem at hand. If you use the Storage façade properly then you can switch what driver it uses by setting the FILESYSTEM_DRIVER environment variable. You can set it to local in your local environment, and s3 on your production server, and your code won’t need to change. Then when you want to generate a URL to a file, you can use the appropriate method:

<img src="{{ Storage::url($imagePath) }}">
1 like
liamseys's avatar

Hi @martinbean

Thanks for your reply! These images are indeed non-static files that are dynamically imported into our system. They should be accessible for the public on our domain.

As you mentioned I switched back to the Storage::url method to serve the images. However Laravel generates the following URL: 'storage/images/test123.jpg, I removed the storage from the URL and then it works. I use the following code for the symlink:

public_path('images') => storage_path('app/images')
liamseys's avatar

@martinbean I have fixed my issue using the following code to store and display the image in our app. Locally it should be saved to my storage/public folder. Do I use the correct solution here or is there a better way?

return Storage::disk(app()->environment('local') ? 'public' : 's3')->url('images/' . $this->images()->first()->file_name);
newbie360's avatar

@liamseys

if you use this code, all the time it need check the disk name, bad performace

instead of this, in production and local .env create a variable, and get it from config() since config is cacheable

Storage::disk(config('something.disk'))
martinbean's avatar
Level 80

@liamseys No, because you’ve just completely ignored what I outlined to you.

You should not have code checking the environment and switching the disk. Use environment variables to choose which disk should be used in each environment.

You’re not writing portable code if you just have a shit-load of if statements or ternary operators all over the place checking the environment etc.

Please or to participate in this conversation.