public/storage软链接必须真实存在且指向../storage/app/public,否则Web服务器无法访问storage/app/public中的文件;需用ls -la验证,缺失或错误则rm -f后php artisan storage:link重建。

public/storage 软链接是否真实存在且指向正确
这是最常见、也最容易被忽略的一环。Laravel 默认把公开图片存到 storage/app/public/,但 Web 服务器只能直接访问 public/ 下的文件 —— 所以必须靠 public/storage 这个软链接“桥接”过去。
排查方法:
- 进项目根目录,运行
ls -la public/storage,确认输出里有类似storage -> ../storage/app/public的指向(不是绝对路径错误,也不是空目录) - 如果不存在,或指向
../storage/app(漏了public)、/var/www/xxx/storage/app/public(硬编码绝对路径),都算错 - 删掉旧链接:
rm -f public/storage - 用 Artisan 重建:
php artisan storage:link(它会自动用相对路径生成,更安全) - 注意:该命令需在 Web 服务器用户权限下运行,否则链接可能被创建但无读取权限
上传路径和 URL 构建是否匹配
很多 404 其实是前端拼错了 URL,而不是文件没加载成功。
关键点:
立即学习“前端免费学习笔记(深入)”;
- 如果你用
Storage::disk('public')->put()上传,文件物理位置是storage/app/public/xxx.jpg,但访问 URL 必须是/storage/xxx.jpg(由软链接暴露) - 如果你用
public_path()直接写入(比如$file->move(public_path('uploads'), $name)),那 URL 就该是/uploads/xxx.jpg,跟storage:link完全无关 - 视图中务必用
asset()或Storage::url(),别手拼字符串:<img src="{{ asset('uploads/' . $item->image) }}">✔️<img src="/uploads/{{ $item->image }}">❌(可能因子目录部署失效)
Nginx/Apache 是否拦截了 /storage 路径或图片后缀
即使软链接存在、URL 正确,Web 服务器配置也可能直接拒绝请求 —— 特别是 Nginx 常见的静态文件 location 规则会误杀。
典型问题:
- Nginx 配置里有类似
location ~ \.(jpg|jpeg|png|gif)$ { try_files $uri =404; },但它只查$uri对应的物理路径,而/storage/xxx.jpg是软链接,不是真实文件路径,try_files会跳过它直接返回 404 - 解决办法:把
/storage加进白名单,或者改用alias显式映射:location ^~ /storage { alias /path/to/your/project/storage/app/public; } - Apache 用户检查
.htaccess是否禁用了FollowSymLinks(导致软链接失效),需确保有Options +FollowSymLinks
storage/app/public 和 public/cache 目录权限是否允许 Web 服务器读取
Linux 下权限不对,软链接存在也白搭 —— Nginx/Apache 进程用户(如 www-data 或 nginx)必须能 traverse 到最终文件。
验证与修复:
- 运行
ls -ld storage app public,确认每级父目录都有x权限(例如drwxr-xr-x),否则无法进入子目录 - 运行
ls -l storage/app/public,确认文件本身有r权限(如-rw-r--r--) - 临时放宽测试:
chmod -R 755 storage/app/public public/storage(上线前应按最小权限原则收紧) - 若用 Bagisto 等扩展框架,还要检查
public/cache目录权限,它的图片重写依赖该目录可写可读
真正卡住人的往往不是某一个环节,而是多个条件叠加:软链接存在但权限不对、URL 拼对了但 Nginx 把请求截胡了、缓存目录可写但 cache 子目录不可读……建议按「链接 → URL → Web 服务器 → 权限」顺序逐层验证,别跳步。


















