Solving the public Prefix Issue in Laravel File Storage

Dr. Adam Nielsen
3 min readJul 9, 2024

Struggling with file paths when uploading files in Laravel? Here’s a quick guide to storing files correctly and retrieving their URLs without hassle. The key is to use the appropriate disk to avoid unnecessary prefixes, making migrations to other storage drivers seamless.

Understanding the Default Disk Configuration

By default, Laravel’s filesystems.php configuration file might look like this:

'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],

'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'visibility' => 'public',
'throw' => false,
],
],

When using the local driver, the returned paths in the file storage methods include a public prefix, which can be cumbersome to handle. This issue also complicates switching to an S3 storage driver. Understanding how to address this is crucial for smooth file management in Laravel.

The Issue

Using Laravel’s store method with the default disk returns a relative path based on the disk configuration. This means files are stored in storage/app/public/avatars, and the returned path includes the public prefix. For example:

public function update(Request $request): string
{
// The returned path is of the form 'public/avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('public/avatars');
// .. store path in model
$user->avatar = str_replace('public/', '', $path);
}

However, in the database, we want to store avatars/ASDASD.jpg without the public prefix because we use it like this: Storage::url('avatars/ASDASD.jpg'). Including the public prefix in the path within store will also cause issues when switching to an S3 storage driver later, as we don’t want the public prefix there. If you change your default storage driver to S3, you would need to change the code to:

public function update(Request $request): string
{
// The returned path is of the form 'public/avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 'S3');
// .. store path in model
$user->avatar = $path;
}

Switching storage driver would suddenly become cumbersome and error-prone.

The Solution

Store files in the public disk explicitly to avoid path issues:

public function update(Request $request): string
{
// The returned path is of the form 'avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 'public');
// store in model..
$user->avatar = $path;
}

This small change is a big win.

  1. We get rid of store('public/avatars') which makes only sense for local driver
  2. We get the clean relative path that should be stored in the database
  3. Changing the storage driver is easy now.

Retrieve File URL:

$avatarPath = 'avatars/XXX.jpg';
$avatarUrl = Storage::url($avatarPath);

echo $avatarUrl; // Outputs /storage/avatars/XXX.jpg for "local" driver

Switching to S3:

Image you want to use now an S3 solution instead of local path.

Configure S3 Disk:

'disks' => [
'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'),
],
],

Update File Upload Method:

public function update(Request $request): string
{
// The returned path is of the form 'avatars/AJDSLAJSD.jpg'
$path = $request->file('avatar')->store('avatars', 's3');
// store in model..
$user->avatar = $path;
}

Retrieve S3 URL:

$avatarPath = 'avatars/XXX.jpg';
$avatarUrl = Storage::disk('s3')->url($avatarPath);

echo $avatarUrl; // Outputs the full S3 URL to the avatar image

By using Laravel’s storage system, you can handle file uploads efficiently and switch storage backends with ease.

--

--