Laravel默认将文件存于storage/app是出于安全性、灵活性和可维护性设计:防止敏感文件被直接访问,支持按需配置公开/私有磁盘,并便于集中管理与备份。

storage/app 是有明确设计意图的,不是疏忽或“不方便”的妥协。直接往 public 目录写文件,会绕过 Laravel 的文件系统抽象层,带来可维护性、部署一致性、安全控制三方面隐患。
为什么 store('uploads') 实际没进 public 目录?
很多人以为 $file->store('uploads') 会自动写入 public/uploads,其实它走的是 config/filesystems.php 中默认磁盘(通常是 local)的 root 配置。默认值是 storage_path('app'),也就是 storage/app/uploads/ —— 这个路径**不对外 Web 可访问**。
- 如果你改了
local磁盘的root为public_path('uploads'),那确实会写进public/uploads,但这是手动破坏默认约定 -
store()方法本身不关心“是否公开”,只认当前磁盘配置 - 用
storeAs('uploads', $name)也一样,路径前缀仍由磁盘 root 决定
想让文件可访问,正确做法是用 public 磁盘 + storage:link
这才是 Laravel 官方推荐、兼顾安全与可用的路径:
- 保持
public磁盘配置不变:'root' => storage_path('app/public') - 上传时显式指定磁盘:
$file->store('uploads', 'public')→ 文件落在storage/app/public/uploads/ - 运行
php artisan storage:link,在public/storage创建指向storage/app/public的符号链接 - 前端通过
/storage/uploads/xxx.jpg访问(注意是/storage/,不是/uploads/)
这个方案的好处是:文件物理位置集中可控、Web 路径统一、上线部署时软链可重做、不同环境(如 Docker 或 Envoyer)行为一致。
直接写 public 目录会踩哪些坑?
看似省事,实则埋雷:
-
public目录常被 Git 忽略(.gitignore里有/public/uploads),导致上传文件无法随代码版本管理,协作或回滚时丢失 - 零停机部署(如 Envoyer)依赖
storage/app持久化,而public下的上传目录每次部署可能被覆盖或清空 - 无法使用
Storage::disk('public')->url()生成带签名的临时 URL(私有文件场景失效) - 如果上传逻辑混用
move()和store(),路径拼接容易出错,比如public_path('uploads') . '/' . $name没做目录创建或权限检查,导致 500 错误
真要放 public 目录,至少守住这三条线
如果业务强要求必须直写 public(例如某些静态站点生成器集成),请手动接管并加固:
- 绝不改
local磁盘的root,而是单独配一个新磁盘,比如webpublic,'root' => public_path('uploads') - 上传前必须调用
Filesystem::makeDirectory()确保子目录存在,Linux 权限设为755,文件为644 - 所有文件名强制重命名(别用
getClientOriginalName()),过滤掉路径遍历字符(../)、空字节、控制符,扩展名白名单校验(in_array($ext, ['jpg','png','pdf']))
最常被忽略的一点:Nginx/Apache 的 location ~ \.php$ 规则若未限制 public/uploads 目录执行 PHP,上传恶意 .php 文件就等于给攻击者开了后门。


















