Deploying a Laravel application on shared hosting can sometimes introduce unexpected issues — especially when working with image uploads and storage paths. One of the most common problems developers encounter is:
Uploaded images work locally but return 404 errors or broken images on production.
This issue is especially common on cPanel shared hosting environments using addon domains, where Laravel’s default storage system behaves differently from a VPS or dedicated server setup.
In this article, we’ll walk through:
- Why uploaded images break on shared hosting
- Common mistakes developers make
- The correct way to upload images in Laravel + Livewire
- How to fix image URLs permanently on cPanel hosting
The Problem
A typical broken image URL looks like this:
https://yourdomain.com/posts/example-image.png
But instead of displaying the image, the browser returns:
404 Not Found
Even worse:
- The image may exist on the server
- Uploads may appear successful
- Everything works locally
This happens because Laravel’s storage system is designed differently from the file structure used by many shared hosting providers.
Why This Happens on Shared Hosting
Laravel normally stores uploaded files here:
storage/app/public
To make these files publicly accessible, Laravel expects you to create a symbolic link using:
php artisan storage:link
This creates:
public/storage → storage/app/public
However, on many cPanel shared hosting providers:
- SSH access may be unavailable
- Symlink creation may be restricted
- Addon domains may not use the standard public_html structure
- public_path() may not point where you expect
The result:
Laravel uploads successfully, but Apache cannot find the image publicly.
Common Mistakes
1. Using Incorrect Blade Syntax
Incorrect:
<img src="{$post->image}}">
Correct:
<img src="{{ asset($post->image) }}">
2. Saving Wrong Paths to Database
Incorrect:
$data['image'] = '/posts/' . $filename;
Correct:
$data['image'] = 'posts/' . $filename;
3. Relying on storage:link on Shared Hosting
On many shared hosting environments, symlinks are unreliable or blocked entirely.
Instead of fighting the hosting configuration, it is often easier and more reliable to upload directly into Laravel’s public directory.
The Best Solution for Shared Hosting + Livewire
The most reliable approach is:
✅ Upload directly into Laravel’s public/posts folder.
This bypasses:
- storage symlink issues
- addon domain path confusion
- Apache access problems
Final Working Laravel + Livewire Upload Solution
Step 1 — Configure a Custom Disk
Open:
config/filesystems.php
Add this inside the disks array:
'posts' => [
'driver' => 'local',
'root' => public_path('posts'),
'url' => env('APP_URL') . '/posts',
'visibility' => 'public',
],
Step 2 — Create the Upload Logic
Inside your Livewire component:
if ($this->newImage) {
$filename = time() . '.' . $this->newImage->getClientOriginalExtension();
// Ensure folder exists
if (!file_exists(public_path('posts'))) {
mkdir(public_path('posts'), 0755, true);
}
// Store directly into public/posts
$this->newImage->storeAs(
'',
$filename,
'posts'
);
// Save image path
$data['image'] = 'posts/' . $filename;
}
Step 3 — Display Images Correctly
In Blade:
<img src="{{ asset($post->image) }}" alt="{{ $post->title }}">
This generates:
https://yourdomain.com/posts/image.png
which Apache can serve directly.
Important Shared Hosting Tips
Use Correct Folder Permissions
Ensure:
/public/posts
has permission:
755
If uploads fail temporarily, try:
775
Clear Laravel Cache After Config Changes
Whenever you modify filesystems.php, clear config cache:
php artisan config:clear
php artisan cache:clear
If SSH is unavailable, use cPanel Terminal or a temporary route.
Why This Approach Works Better on cPanel
Uploading directly into the public directory:
- avoids symlink problems
- avoids addon-domain path confusion
- works consistently on Truehost, Namecheap, Hostinger, and similar shared hosts
- simplifies image URLs
- reduces deployment headaches
For small to medium Laravel applications on shared hosting, this method is often the most practical and stable solution.
Final Thoughts
Laravel’s storage system is excellent on VPS and cloud infrastructure, but shared hosting environments often require a more direct approach.
If your Laravel images:
- work locally but not on production
- return 404 errors
- break on addon domains
- fail with storage:link
then uploading directly into Laravel’s public folder is usually the cleanest fix.
With the setup above, your uploads will work reliably across most cPanel shared hosting providers.