Laravel image not shown on server and working fine on my local machine
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Laravel Image Not Showing on Server: The Local vs. Production Storage Dilemma
Dealing with discrepancies between a local development environment and a live server is one of the most frustrating hurdles in web development. You build, test, and see everything work perfectly on your machine, but when you deploy to the server, assets like images fail to load, often resulting in NotFoundHttpException errors.
This post dives deep into the common cause of this problem in Laravel applications—specifically how file paths and public storage are handled during deployment—and provides a concrete, developer-focused solution.
The Mystery of Missing Images: Local vs. Server Reality
The scenario you described—where images display fine locally but fail on the server—is almost always a configuration or linking issue related to how Laravel's Storage mechanism interacts with the web server's file structure.
When you run Laravel locally, development servers often have relaxed permissions or custom configurations that allow direct access to files within the storage directory. However, when deploying to a production environment (especially via shared hosting setups like /public_html), the web server (Apache or Nginx) strictly enforces security permissions and path resolution based on the deployed file structure.
In your case, even though you correctly define the storage paths in filesystems.php, the web server might not have the necessary link established to map the public URL (/storage/...) back to the actual physical files located within storage/app/public.
Diagnosing Your Filesystem Setup
Let's examine the configuration you provided:
// filesystems.php excerpt
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'), // This points to storage/app/public
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
],
And your access attempt: <img src="storage/{{ $ad->avatar }}"> which resolves to http://compare.theofflinemall.com/storage/featured_image/....
The problem lies in the gap between the logical path Laravel generates and the physical location where the web server can read those files. The URL generated (/storage/) needs a corresponding symbolic link on the public-facing directory for the web server to successfully resolve the request to the actual file system.
The Definitive Solution: Utilizing Symbolic Links
The universally accepted, robust way to make Laravel storage accessible via a public URL is by creating a symbolic link (symlink) from your public directory to your storage/app/public directory. This tells the operating system that anything requested in the public/storage folder should be read directly from the actual files in storage/app/public.
Follow these steps to implement the fix:
Step 1: Run the Artisan Command
Navigate to your project root in your terminal and execute the following command. This command creates the necessary link:
php artisan storage:link
This command creates a symbolic link at public/storage that points directly to storage/app/public.
Step 2: Verify Permissions (Crucial for Deployment)
Ensure that your web server user (e.g., www-data or apache) has read and execute permissions on the storage directory structure. If you are on a shared host, permissions are often the culprit. Ensure that ownership and read access are correctly set up for the files inside storage/app/public.
Step 3: Update Your Code (Best Practice)
While your image retrieval syntax (storage/{{ $ad->avatar }}) is correct when using the Storage Facade, always ensure you are leveraging Laravel's built-in tools. For accessing files, use the Storage facade instead of raw string paths where possible to manage path generation cleanly.
Example of Accessing Files:
use Illuminate\Support\Facades\Storage;
// To get a file from your public disk:
$path = 'featured_image/qzHWpQeSfKjZ6DOS59ROyYboJ1GCvVi6NNVfLtVV.jpeg';
$imageData = Storage::disk('public')->url($path);
// Or in a Blade view, if you are using the public disk:
<img src="{{ Storage::url('featured_image/qzHWpQeSfKjZ6DOS59ROyYboJ1GCvVi6NNVfLtVV.jpeg') }}" alt="Image">
Conclusion
The issue you faced is a classic deployment hurdle, not a bug in your application logic. By understanding the separation between the application's internal structure and the web server's public view, we can apply the correct Laravel methodology. Implementing the php artisan storage:link command ensures that your application correctly bridges the gap between the file system and the web request handler. Always rely on Laravel's built-in features, like those found in Laravel, to manage assets efficiently and reliably across all environments.