Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Viadsss's avatar

Accessing image in public still works without symlink (Explanation please)

<?php
class RegisteredUserController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'lowercase', 'email:strict,dns', 'max:255', 'unique:' . User::class],
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
            'profile' => ['required', 'image', 'mimes:jpeg,png', 'max:2048'],
        ], [
            'profile.max' => 'The profile image must not be larger than 2MB.',
        ]);
        if ($request->hasFile('profile')) {
            $profilePath = $request->file('profile')->store('profiles', 'public');
        }
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
            'profile' => $profilePath,
        ]);
        event(new Registered($user));
        Auth::login($user);
        return redirect(route('dashboard', absolute: false));
    }
}

example profile value in user: profiles/CKyXwwZFVGJ5ghhkfUBQfXDsMgw4eliC2iz8YFLN.jpg

why i can do thses 3 without even php artisan storage:link and they still works, i can still see the image?:

<img src="{{ auth()->user()->profile }}" />
<img src="{{ asset(auth()->user()->profile) }}" />
<img src="{{ asset('storage/' . auth()->user()->profile) }}" />

the image is stored in the `storage/app/public/profiles/filename.jpg' (note that i haven't symlink it yet and i haven't configure the filesystems.php)

Update (1 day after the post):

I created another project and tested this again, the same setup, laravel new imagebugtest no starter kit, then i composer require laravel/breeze --dev then php artisan breeze:install

Then in dashboard.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <img src={{ auth()->user()->profile }} />
                <img src={{ asset(auth()->user()->profile) }} />
                <img src={{ asset('storage/' . auth()->user()->profile) }} />
                <div class="p-6 text-gray-900">
                    {{ __("You're logged in!") }}
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

corresponds to the following src when inspected:

<img src="profiles/dmciGSr4LOEXVcyr5iIpyU3b14eWEEFzuvYHCIzz.png">
<img src="http://imagebugtest.test/profiles/dmciGSr4LOEXVcyr5iIpyU3b14eWEEFzuvYHCIzz.png">
<img src="http://imagebugtest.test/storage/profiles/dmciGSr4LOEXVcyr5iIpyU3b14eWEEFzuvYHCIzz.png">

2nd Update (1.5 Days after the post):

Since I am just using npm run dev or npm run build to view the website, since composer run dev is clunky in Herd (the port is not displaying when it tries to run the php artisan serve part of that command)

I tried first php -S localhost -t public and I can see that the three images doesn't work (which is intended) then after i php artisan storage:link the 3rd img tag with

<img src={{ asset('storage/' . auth()->user()->profile) }} />

only displays the image!

I also tried to use the `php -d variables_order=GPCS artisan serve (not sure why i need to include the -d variables_order=GPCS, i found that this is a problem in herd, and why this is not the default if this is the working solution lol)

the result of this is similar when i serve the project with PHP's built-in server.

I think the problem is with the Herd itself (unlikely) or the way i installed Herd or PHP.

1 like
16 replies
jlrdw's avatar

Because they are in a web folder.

If you have private user images don't expose them to the web.

Viadsss's avatar

@jlrdw yup, i want them to store their profile in to the storage/app/public/profiles/ directory, since this would be their profile picture. I just don't get why this 3 displays the image properly:

<img src="{{ auth()->user()->profile }}" />
<img src="{{ asset(auth()->user()->profile) }}" />
<img src="{{ asset('storage/' . auth()->user()->profile) }}" />

when it should not display anything since i haven't put an image in the public/ directory and i haven't create a symbolic link yet.

jlrdw's avatar

@Viadsss if they are under public/ you don't need a symbolic link. Anything under public is already a web folder.

A symbolic link is if they are out of a web accessible.

Snapey's avatar

what value is in the database?

what url is generated in the web page (view source in browser)

1 like
Viadsss's avatar

Hello @Snapey, Thank you for taking an interest on this post

<img src="{{ auth()->user()->profile }}" />
<img src="{{ asset(auth()->user()->profile) }}" />
<img src="{{ asset('storage/' . auth()->user()->profile) }}" />

corresponds to the following URL

http://homeroom.test/profiles/CKyXwwZFVGJ5ghhkfUBQfXDsMgw4eliC2iz8YFLN.jpg
http://homeroom.test/profiles/CKyXwwZFVGJ5ghhkfUBQfXDsMgw4eliC2iz8YFLN.jpg
http://homeroom.test/storage/profiles/CKyXwwZFVGJ5ghhkfUBQfXDsMgw4eliC2iz8YFLN.jpg

I know that i should use storage:link since i am storing the image in the public disk, i even look for the public/storage/ directory to make sure that i haven't symlink already. So this seems a bit odd

$ ls -la public
total 19
drwxr-xr-x 1 My Name 197609   0 May 20 18:44 ./
drwxr-xr-x 1 My Name 197609   0 May 19 21:14 ../
-rw-r--r-- 1 My Name 197609 740 May 11 18:55 .htaccess
drwxr-xr-x 1 My Name 197609   0 May 20 17:29 build/
-rw-r--r-- 1 My Name 197609   0 May 11 18:55 favicon.ico
-rw-r--r-- 1 My Name 197609  17 May 20 18:08 hot
-rw-r--r-- 1 My Name 197609 543 May 11 18:55 index.php
-rw-r--r-- 1 My Name 197609  24 May 11 18:55 robots.txt
drwxr-xr-x 1 My Name 197609   0 May 20 18:44 vendor/

Here is my filesystems.php

I am using Laravel Herd if it affects something

Snapey's avatar

and your images are definately saved to the app/public/profiles folder?

What OS is it?

1 like
Viadsss's avatar

@Snapey I am using Windows 11 with Laravel Herd, the image is stored in the storage/app/public/profiles/hashname.jpg

Snapey's avatar

images aren't just being cached in your browser from earlier tests? open devtools and turn off caching and reload the page?

1 like
Viadsss's avatar

@Snapey thank you for suggestion. as of now i tested this and even tried different browser and incognito mode but still the same, i can still see the image on those 3 without symlinking and tried another install for 3rd time without starter kit and breeze but still the same outcome.

Viadsss's avatar

@Snapey I think I’ve figured out why the images were displaying. I’ve updated my post with the details. Thank you for helping me eliminate potential causes along the way — really appreciate it!

Snapey's avatar

@Viadsss Why do you 'use' herd then not use it for its web server and domain name functions?

Viadsss's avatar

@Snapey That's what I am trying to do. I just followed Jeffrey's 30 Days of Laravel and created some baby projects with this framework. I did not know that there were some gotchas that I needed to look out for, such as my concern about public storage.

For the composer run dev part, I have the same problem as this Stack Overflow Question, so I can't do composer run dev or php artisan serve directly. Not sure why this is the case for Laravel Herd on Windows.

Please or to participate in this conversation.