Laravel Filesystem Storage Disks File Uploads Signed Urls
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Laravel menyediakan abstraksi filesystem lewat Storage. Intinya, Laravel memisahkan cara aplikasi menyimpan file dari lokasi fisik penyimpanan.
Kamu bisa menulis kode yang sama untuk:
Dengan abstraksi ini, controller dan service kamu tidak perlu tahu file disimpan di folder lokal, bucket cloud, atau storage yang dienkripsi.
Pakai storage disk kalau file tidak cukup disimpan sebagai string di database, atau kalau kamu butuh perilaku storage yang berbeda berdasarkan jenis file.
Storage disk adalah konfigurasi target penyimpanan.
Di Laravel, disk biasanya didefinisikan di config/filesystems.php, misalnya:
local untuk storage lokal non-publicpublic untuk file yang dibuka via web servers3 untuk object storageYang penting bukan nama disk-nya, tapi tujuan dan policy aksesnya.
use Illuminate\Support\Facades\Storage;
Storage::disk('public')->put('avatars/user-1.jpg', $contents);
Storage::disk('s3')->put('reports/2026/q1.pdf', $contents);Kalau kebutuhan akses berubah, kamu biasanya cukup ganti disk atau konfigurasi, bukan refactor seluruh alur upload.
Upload aman bukan cuma soal menerima file, tapi juga soal membatasi tipe, ukuran, lokasi simpan, dan cara aksesnya.
Gunakan validasi Laravel seperti:
$request->validate([
'file' => ['required', 'file', 'max:5120', 'mimes:jpg,jpeg,png,pdf'],
]);Poin penting:
file memastikan input benar-benar file uploadmax membatasi ukuranmimes atau mimetypes membatasi jenis fileJangan mengandalkan ekstensi file saja. Ekstensi bisa menipu.
Nama asli file bisa berisi spasi, karakter aneh, path traversal, atau konflik nama.
Lebih aman kalau kamu:
Contoh:
$path = $request->file('avatar')->storePublicly('avatars', 'public');Atau kalau ingin kontrol nama:
$file = $request->file('avatar');
$name = $file->hashName();
$path = $file->storeAs('avatars', $name, 'public');Kalau file boleh dibuka langsung oleh browser, taruh di disk yang memang didesain untuk public access.
Kalau file sensitif, jangan taruh di public disk. Simpan di private disk lalu layani lewat controller atau signed URL.
Biasanya yang disimpan di database bukan file-nya, tapi:
Ini memudahkan audit, reprocess, dan cleanup.
Kalau user mengganti avatar atau menghapus dokumen, file lama harus ikut dihapus kalau memang tidak dipakai lagi.
Kalau tidak, storage akan terus membengkak.
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
public function store(Request $request)
{
$validated = $request->validate([
'document' => ['required', 'file',
Untuk file yang memang harus bisa diunduh publik, gunakan disk dan folder yang tepat, bukan sekadar store() lalu berharap URL-nya bisa dibuka.
Ada dua pola utama:
Cocok untuk:
Contoh:
$url = Storage::disk('public')->url($path);Tapi pola ini hanya aman kalau file tersebut memang boleh diakses siapa saja.
Cocok untuk:
Contoh:
public function download(string $path)
{
abort_unless(auth()->user()->can('download-reports'), 403);
return Storage::disk('private')->download($path);
Pola ini memberi kontrol penuh, tapi kamu harus memikirkan otorisasi, streaming, dan caching sendiri.
Signed URL dipakai untuk memberi akses file atau route untuk waktu terbatas tanpa membuka resource secara publik selamanya.
Di Laravel ada dua model umum:
Pakai signed URL kalau:
use Illuminate\Support\Facades\URL;
$url = URL::temporarySignedRoute(
'documents.download',
now()->addMinutes(15),
['document' => $document->id]
);Lalu route-nya divalidasi dengan middleware signed:
Route::get('/documents/{document}/download', [DocumentController::class, 'download'])
->name('documents.download')
->middleware('signed');Kalau signature atau expiry tidak valid, Laravel akan menolak request.
Untuk storage driver yang mendukung temporary URL:
$url = Storage::disk('s3')->temporaryUrl(
$path,
now()->addMinutes(10)
);Ini cocok untuk private object storage yang sudah punya mekanisme signed access di level storage.
Kalau kamu butuh kontrol otorisasi yang kompleks, signed route biasanya lebih mudah dirawat.
Kalau kamu butuh download langsung dari bucket, temporary storage URL sering lebih pas.
Di Laravel, visibility bukan detail kosmetik. Itu menentukan siapa yang bisa baca file.
public berarti file memang boleh diakses browserprivate berarti file harus lewat mekanisme kontrol aksesKalau file dipindah atau rename di storage, pastikan database ikut diperbarui.
Jangan hardcode URL final di banyak tempat. Simpan path logis dan generate URL saat dibutuhkan.
Local disk, symlink public, dan S3 sering kelihatan mirip di development, tapi perilakunya beda di production.
Contoh jebakan:
public/storage, tapi bucket cloud butuh konfigurasi lainJangan baca file besar ke memory tanpa alasan.
Untuk file besar, gunakan streaming atau response download dari storage.
Production storage sering bocor bukan karena upload gagal, tapi karena delete dan replace tidak membersihkan file lama.
Buat aturan:
Ekstensi .jpg tidak otomatis berarti file itu aman. Selalu validasi dari sisi Laravel dan, kalau perlu, cek MIME/content.
Begitu file ada di public storage, siapa pun yang tahu URL bisa mencoba akses.
Upload besar bisa memakan bandwidth, disk, dan waktu proses. Batasi di aplikasi dan di server.
Nama file yang sama dari banyak user bisa saling timpa kalau naming strategy buruk.
Kalau database dipakai untuk menyimpan isi file, backup dan query jadi berat. Simpan binary di storage, metadata di database.
Ini jebakan yang paling sering muncul saat edit profile, ganti dokumen, atau regenerate asset.
Expiry yang terlalu panjang mengurangi nilai signed access. Kalau file sensitif, pilih expiry singkat dan policy yang jelas.
Jangan cek berhasil hanya di local. Pastikan flow juga jalan di storage target production.
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
public function show(Document $document)
{
abort_unless(auth()->id() === $document->