In this laravel tutorial you will learn how to display image stored in laravel’s storage folder with step by step example.
When working with file uploads in Laravel, one of the most common tasks is displaying images stored in the storage folder. In this tutorial, we’ll walk through the step-by-step process of uploading and displaying images from the storage directory in a Laravel 12 application.
Table of Contents
Laravel 12 Storage Architecture
Laravel organizes file storage into different disks, each serving specific purposes:
- Local disk: Stores files in storage/app directory (private by default)
- Public disk: Stores files in storage/app/public directory (intended for web-accessible files)
- S3 disk: Stores files in Amazon S3 cloud storage
The key distinction is that files in the public disk are meant to be accessible via web URLs, while files on the local disk remain private unless explicitly served through controllers.
Method 1: Public Access Using Symbolic Links
By default, images uploaded using the public disk are stored in the storage/app/public directory.
To make these images accessible publicly, run this command:
php artisan storage:link
This creates a symbolic link(shortcut) inside your public/storage folder that points to the storage/app/public directory. Laravel does not copy or duplicate files.
- Files are really saved in storage/app/public/
- A shortcut (symlink) is created at public/storage/
- When you access /storage/images/photo.jpg, Laravel redirects it to /storage/app/public/images/photo.jpg


Step 1: Store Images on Public Disk
When uploading files, specify the public disk:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Models\User;
class UserController extends Controller
{
public function store(Request $request)
{
$request->validate([ 'image' => 'required|mimes:jpg,png,jpeg',]);
// Saving an uploaded image
$imagePath = $request->file('image')->store('images','public');
//Save path to database
auth()->user()->update(['avatar'=>$imagePath]);
return back()->with('success','Image Uploaded Successfully!');
}
}
Step 2: Display Images Using Storage Facade
Retrieve image URL in controller, Use Laravel’s storage Facade generate proper image URLs. The Storage::url() method automatically generates the correct URL path that maps through the symbolic link.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Models\User;
class UserController extends Controller
{
public function displayImage()
{
$imageUrl = Storage::url('images/9FOxDeLj1oTMOuEpInT2sZSj4RsJQ0QNTFkboCcz.png');
return view('post',compact('imageUrl'));
}
}
Display image in blade view (Using Controller Variable)
<img class="mb-3 image_gallery" src=" {{asset($imageUrl)}}" id="image_gallery">
- $imageUrl comes from the controller.
- asset() converts it to a full public URL (e.g., https://yourapp.com/storage/images/…).
Directly Use Storage Facade in Blade
You can directly display image using Storage facade in blade template :
<img src="{{ Storage::url('images/photo.jpg') }}" alt="Photo" class="img-fluid">
This is simpler if the filename is known.
Use Public Asset Path
If your image is already available in the public/storage folder, you can also reference it directly:
<img class="mb-3 image_gallery" src=" {{asset('storage/images/photo.png')}}" id="image_gallery">
This works because asset(‘storage/…’) points to files inside public/storage.
Read Also : Laravel 12 Validation Rule Sometimes vs Nullable
Method 2: Secure Image Streaming Through Controllers
Step 1: Create Image Controller
For images requiring access control or enhanced security, stream them through dedicated controllers:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Models\User;
class UserController extends Controller
{
public function displayImage($imageName)
{
$path = 'images/'.$imageName;
// Verify file exists
if (!Storage::disk('public')->exists($path)) {
abort(404, 'Image not found');
}
// Get file contents and MIME type
$file = Storage::disk('public')->get($path);
$type = Storage::mimeType($path);
return response($file, 200)->header('Content-Type', $type);
}
}
Inside this function, you build the file path (like images/photo.png), check whether the file exists using Storage::disk(‘public’)->exists(), and if it doesn’t, you return a 404 “Image not found” error. If the file does exist, you read its content using Storage::get() and also get its MIME type (like image/png or image/jpeg) with Storage::mimeType(). Finally, you return the image as a response with the correct content type header using Laravel’s response() helper.
Step 2: Define Secure Routes
You also define a route such as :
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('img/{imageName}', [UserController::class,'displayImage'])->name('img');
This route points to the controller method, and you can then use it in your Blade template to display the image :
<img src="{{ route('img', ['imageName' => 'photo.png']) }}" alt="User Photo">
This approach ensures complete access control while maintaining proper MIME type headers for browser compatibility.
Read Also : Laravel 12 Custom Validation Error Messages Example
Laravel 12 Display Image From Storage Folder
Common Issues
Image not showing?
Make sure you have created the symbolic link using: php artisan storage:link
Permission denied error?
Ensure your storage folder has the correct permissions: chmod -R 775 storage
Wrong path?
Always use asset(‘storage/’.$filename) instead of storage_path() when displaying images in Blade templates.
Conclusion
Laravel 12 offers multiple robust approaches for displaying images from storage folders. The symbolic link method works excellently for public images with optimal performance, while controller-based streaming provides superior security for sensitive content. Choose the approach that aligns with your application’s security requirements and performance needs.
Always implement proper file validation, error handling, and follow Laravel’s security best practices to ensure your image display functionality remains both functional and secure.
Frequently Asked Questions (FAQs)
Q1: Why can’t I access images directly from the storage folder?
By default, Laravel’s storage folder is not publicly accessible for security reasons. Files stored in storage/app/public can only be viewed on the web after creating a symbolic link using the command php artisan storage:link.
Q2: What is the difference between Storage::url() and asset() when displaying images?
Storage::url() automatically generates the correct file path based on your configured storage disk, while asset() simply converts a relative path to a full public URL. Both work, but Storage::url() is preferred when working with Laravel’s filesystem disks.
Q3: Which method should I use — symbolic link or controller streaming?
Use symbolic link method for normal, public images (like profile photos or blog thumbnails).
Use controller-based streaming for private or sensitive files that should only be visible to authorized users.
Q4: What are MIME types, and why are they important?
A MIME type (like image/png or image/jpeg) tells the browser how to interpret and display the file. When streaming images through a controller, setting the correct MIME type ensures that the browser can display the image properly.