I save uploads with $request->file('photo')->store('photos', 'public') and the database holds photos/abc.jpg, but asset('storage/photos/abc.jpg') gives 404. What am I missing?
DEV3 Developer Forum
Why do uploaded images return 404 in Laravel and what does storage:link do?
Ask a question
Every post is reviewed first. Usually reviewed within 24 hours.
dev3 ·
0
1 reply
dev3 · Accepted answer
Files stored on the public disk live in storage/app/public, which is not web-accessible. php artisan storage:link creates a symlink public/storage pointing to it, so the browser can reach the files.
php artisan storage:link
Then generate URLs with the Storage facade rather than hardcoding paths:
$path = $request->file('photo')->store('photos', 'public');
$url = Storage::disk('public')->url($path); // /storage/photos/abc.jpg
Common reasons it still 404s
- The link was created on your laptop and not on the server. Run the command on every environment (the symlink is not part of Git,
public/storageshould be in.gitignore). APP_URLis wrong, sourl()points at another host or lacks the port. Set it correctly.- On Windows or Docker, the symlink made on the host is broken inside the container. Run the command inside the container.
- Shared hosting that disallows symlinks or has
public_htmlseparate from the app: either create the link with a tiny PHP script usingsymlink(), or define a custom disk whoserootis inside the web root:
// config/filesystems.php
'disks' => [
'uploads' => [
'driver' => 'local',
'root' => base_path('../public_html/uploads'),
'url' => env('APP_URL').'/uploads',
'visibility' => 'public',
],
],
- Nginx: a
location ~ /\.rule or missingtry_filescan block it; check the actual file exists by listing the target folder.
Private files (invoices, IDs) should stay on the local disk and be served through a controller or a temporary signed URL, never through the public symlink.
See the signed temporary download URL snippet for the private case.
0