Download Files in Laravel
Content Index
- 1 Introduction: the different ways to download files in Laravel
- 2 Download from the public folder
- 3 Download from a protected disk
- 4. Advanced downloads: streaming and remote files
- Streaming with streamDownload()
- Downloading from Amazon S3 or Google Cloud
- 5. How to allow downloads only to authenticated users
- Extra: DON'T use the public folder for uploading certain files, protect your files in Laravel's storage
- Protected Files: don't use the public folder
- Storage Disk Configuration
- File Upload
- Access Control for Download
- Complete Secure Download Function
- Benefits of this Scheme
- 6. Common errors when downloading files in Laravel (and how to avoid them)
- 7. Conclusion and final tips
- Frequently Asked Questions
How to download a file in Laravel? It's one of those tasks that seems simple once you already know how to upload files in Laravel… and it is, as long as you know where you want to perform the download from.
In my case, when I started working with projects that served digital books, I found out that offering a public file was not the same as offering a protected one.
In this guide, I explain both options —and some more advanced ones— with real examples and practical advice.
In both cases, we use the download() method, which indicates the path of the file to be downloaded.
1 Introduction: the different ways to download files in Laravel
Laravel gives us several ways to serve files to the user:
From the public folder, for files directly accessible by the browser.
From a disk (Storage), when you need access control or privacy.
Via streaming or from external services like Amazon S3.
The important thing is to understand the logic: the download() method generates an HTTP response that forces the download in the browser, without exposing the actual physical path of the file.
2 Download from the public folder
If the file is in public, you can simply return the path to that file and let the browser handle everything.
This case is the simplest, as the public folder is the only one that is directly accessible from the browser:
public function download($file_name) {
$file_path = public_path('files/'.$file_name);
return response()->download($file_path);
}Laravel converts that path into a response ready for download.
It's the method I usually use for open resources, such as free guides or templates.
Advantages:
- Simple and direct.
- Requires no additional configuration.
Limitations:
- Not secure for sensitive files.
- You cannot control who accesses the resource.
3 Download from a protected disk
In this example, I use a disk because it's a protected resource.
Remember that all folders outside of public are private, which makes them ideal for storing files to which we do not want to grant direct access, but controlled access.
Notice here: the disk is local, although you could also use Amazon or other providers:
config\filesystems.php
'files_disk' => [
'driver' => 'local',
'root' => app()->storagePath()
],The path, again, is local, and to organize them, I use a root path, where I save all the books that, based on some logic (for example, when you buy them), are the ones I allow to be downloaded.
The process is simple:
Storage::disk('files_disk')->download($routefile);
Storage::disk('files_disk')->download('book/' . $file->file, $name . "." . $file->type);In my case, this approach is much safer.
Everything outside of public is inaccessible from the browser, which ensures that only authorized users can access it.
Personal Tip: I usually maintain a clear folder structure (books/, images/, invoices/) to avoid confusion and improve traceability.
4. Advanced downloads: streaming and remote files
If you work with large files or want to download from external providers, Laravel offers other powerful tools.
Streaming with streamDownload()
It allows sending the file in parts, ideal for heavy files:
return response()->streamDownload(function () use ($path) {
echo Storage::disk('files_disk')->get($path);
}, 'archivo.pdf');Downloading from Amazon S3 or Google Cloud
You only need to configure the disk in filesystems.php and use it the same way as the local one:
return Storage::disk('s3')->download('docs/manual.pdf');You can also force a different download name or a specific MIME type with headers:
return response()->download($path, 'mi-archivo.pdf', [
'Content-Type' => 'application/pdf',
]);In my experience, this approach is excellent when managing catalogs of dynamic files or backup copies.
5. How to allow downloads only to authenticated users
Security is key.
You can control who downloads a file simply by protecting the route with middleware:
Route::get('/download/{file}', [FileController::class, 'download'])
->middleware('auth');Or, validate within the controller:
if (auth()->user()->hasPurchased($file->id)) {
return Storage::disk('files_disk')->download($file->path);
}
abort(403, 'No autorizado');This way, you ensure that every download has business logic behind it.
In my case, this was essential when I implemented digital product downloads with license control.
Extra: DON'T use the public folder for uploading certain files, protect your files in Laravel's storage
File downloading is a very common feature in software development. Allowing users to download files based on specific business rules of your application is common, for example, when you sell files hosted in the application and, once acquired, users can download them.
For this, the application first verifies the payment and then allows the download.
Protected Files: don't use the public folder
It's important to note that files should not be hosted in the public folder, as happens with images loaded through upload processes. Anyone who knows the file name could access it without restrictions.
In Laravel, the only publicly accessible folder is public. Therefore, for files with controlled access, it's not advisable to use this folder.
We can store files in any other application folder, which is useful for scenarios where we want to control access, such as in the storage folder.
Storage Disk Configuration
We create a specific disk to store the protected files:
// config/filesystems.php
'files_sell_uploads' => [
'driver' => 'local',
'root' => app()->storagePath(),
],File Upload
The function for uploading a file might look like this:
function uploadBook()
{
$this->rules = [
'fileBook' => 'nullable|mimes:epub,pdf|max:20024'
];
$this->validate();
if ($this->fileBook) {
$name = time() . '.' . $this->fileBook->getClientOriginalExtension();
$this->fileBook->storeAs('book', $name, 'files_sell_uploads');
YourModel::create([
'file' => $name,
'type' => $this->fileBook->getClientOriginalExtension(),
// otros campos según tu modelo
]);
}
}Access Control for Download
To allow download only to users who meet certain conditions, you can use something like:
if ($filePayment && $file) {
return Storage::disk('files_sell_uploads')->download('book/' . $file->file, "book." . $file->type);
}It's important to highlight that the only way to access these files via HTTP is through the function above. Since they are stored outside of public, the files are not directly accessible.
Complete Secure Download Function
public function downloadFile(File $file)
{
$user = auth()->user() ?? auth('sanctum')->user();
$filePayment = FilePayment::where(condición)->first();
$file = File::where(condición)->first();
if ($filePayment && $file) {
return Storage::disk('files_sell_uploads')->download('book/' . $file->file, "book." . $file->type);
}
return response()->errorResponse("", 403, 'Producto no adquirido o no existe');
}With this implementation, files are only downloaded if the business conditions are met (for example, the user purchased the file).
Benefits of this Scheme
This approach is ideal if you want to develop an online store within your Laravel application, where the files to be sold are securely stored and access is fully controlled from your code.
6. Common errors when downloading files in Laravel (and how to avoid them)
Error Cause Solution
File not found Poorly constructed path or incorrect disk Check storage_path() or disk()
Empty or corrupted download Incorrect encoding or output before the response Make sure you don't have echo or dd() before return
Permission denied Folder without read permissions Correct permissions with chmod or chown
Incorrect name Second parameter not specified in download() Add $name and extension manually
Tip: always use absolute paths (public_path(), storage_path()) to avoid errors in different environments.
7. Conclusion and final tips
Downloading files in Laravel is as simple or as robust as you need.
For public files, response()->download() is enough.
If you handle sensitive data or need control, use disks in Storage and protect the routes.
And if you work with large files or external services, streamDownload() and S3 are your allies.
In my experience, the key is to understand the context of security and optimization, rather than the function itself.
Laravel gives you all the tools; the difference lies in how you combine them.
Frequently Asked Questions
What is the difference between public_path() and storage_path()?
public_path() points to the folder accessible by the browser; storage_path() is private and only accessible via code.
Can I download multiple files at once?
Yes, you can use a library like ZipArchive to generate a .zip and return it with response()->download().
Can the file name be forced?
Yes, by using the second parameter of the download() method, for example:
response()->download($path, 'mi-archivo.pdf').
The next step is to optimize your queries by learning how you can use the cache in Laravel.
I agree to receive announcements of interest about this Blog.
Learn how to download files in Laravel step-by-step: from public paths to protected disks, S3 storage, and streaming downloads. Includes real-world examples, security best practices, and functional PHP code.