Make sure your folder structure is correct and point to public as document root.
See https://laracasts.com/discuss/channels/servers/deploy-on-shared-server
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I have a Laravel 11 app deployed to Hostinger shared webhosting. The images are not being displayed at all and are returning a 404. One of the links is https://viveaventuras.net/destination/san-juan-puerto-rico where the image returns a 404. in my config/filesystems.php, I've ensured that the root for my public disk, is pointing to the public_html in the shared hosting, like so:
cat filesystems.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application for file storage.
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Below you may configure as many filesystem disks as necessary, and you
| may even configure multiple disks for the same driver. Examples for
| most supported storage drivers are configured here for reference.
|
| Supported drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver'=>'local', // I even changed this to 'public' without success
'root' => '/home/u664706382/domains/viveaventuras.net/public_html/storage/app/public',
'url'=>env('APP_URL').'/storage/',
'visibility'=>'public',
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
the images get saved here: /home/u664706382/domains/viveaventuras.net/public_html/storage/app/public/booking_photos but aren't being loaded properly.
Here's the Livewire Backend code responsible for uploading the images:
if (!empty($this->tripPhotos) && is_array($this->tripPhotos)) {
\Log::info('User selected new pictures for upload. Iterating over new pictures..');
foreach ($this->tripPhotos as $photo) {
\Log::info('Checking if user selected pictures');
if ($photo instanceof \Livewire\Features\SupportFileUploads\TemporaryUploadedFile) {
$image = $photo->getRealPath();
$fileName = $photo->hashName() . '.' . $photo->extension();
// $filePath = 'booking_photos/' . $fileName;
// $fullPath = storage_path('app/public/' . $filePath);
$filePath = 'booking_photos/'.$fileName;
$storagePath = Storage::disk('public')->path($filePath);
// \Log::info('Resizing Image...');
// Helper::resizeImage($image, $fullPath, 525, 351);
// Save the image to the file system
$photo->storeAs('booking_photos', $fileName, 'public');
$imageURLs[] = asset(Storage::url($filePath));
$imagesArray[] = $fileName;
\Log::info('Current image URLs array: ' . json_encode($imageURLs));
\Log::info('Current images in the array: ' . json_encode($imagesArray));
}
}
}
The photos get saved in the DB like so:
$tripModel->tripPhoto = json_encode($imagesArray);
$tripModel->save();
on the front-end, here are how the images are displayed on the /destination/{destination-slug} page:
<!-- Photo Grid Section -->
<div class="photo-grid">
@if (!empty($tripPhotos))
<div id="carouselExample" class="carousel slide" data-bs-ride="carousel" data-bs-interval="false"
style="border-radius: 8px;">
<div class="carousel-inner">
@foreach ($tripPhotos as $index => $photo)
<div class="carousel-item {{ $index === 0 ? 'active' : '' }}">
<img srcset="{{ $photo }}" class="d-block w-100"
alt="Photo {{ $index + 1 }}">
</div>
@endforeach
</div>
{{-- Carousel navigation buttons --}}
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExample"
data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carouselExample"
data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
@else
<div class="photo-item">
<img src="{{ asset('assets/images/image_placeholder.jpg') }}" class="d-block w-100"
style="height: 300px;" />
</div>
@endif
</div>
<!-- End Photo Grid Section -->
Please or to participate in this conversation.