Q&A
How to allow only authenticated users access to files in Laravel
Overview
Files placed in public/ are accessible to anyone with the URL - there's no auth check. To restrict access, files must live outside the web root and be delivered through an authenticated Laravel route that confirms the user has permission before streaming the file. The framework middleware handles the auth gate; the controller handles the file delivery.
graph TD
A[User requests file URL] --> B[Laravel route hit]
B --> C[auth middleware: authenticated?]
C -->|no| D[Redirect to login / 401]
C -->|yes| E[Controller checks permission]
E --> F["Storage::download() streams file"]
F --> G[File bytes sent to user]
style C fill:#1e1b4b,color:#fff
Step 1: Store Files Outside the Public Root
Use the local (private) disk which defaults to storage/app:
use Illuminate\Support\Facades\Storage;
// When uploading:
$path = $request->file('document')->store('documents', 'local');
// Stored at storage/app/documents/filename.pdf
// NOT accessible via URL - no web server path to it
storage/app/ is outside public/, so there's no direct URL.
Step 2: Define a Protected Route
Wrap the download route in the auth middleware:
// routes/web.php
use App\Http\Controllers\FileController;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth'])->group(function () {
Route::get('/files/{filename}', [FileController::class, 'download'])
->name('file.download');
});
Any unauthenticated request to this route is redirected to the login page automatically.
Step 3: Stream the File in the Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class FileController extends Controller
{
public function download(Request $request, string $filename)
{
// Build the storage path
$path = 'documents/' . $filename;
// Confirm the file exists
if (!Storage::disk('local')->exists($path)) {
abort(404);
}
// Return the file as a download
return Storage::disk('local')->download($path);
// Or stream inline (e.g. PDF preview):
// return response()->file(storage_path('app/' . $path));
}
}
Step 4: Per-User Authorization (Gates / Policies)
For ownership or role checks beyond "logged in":
// In a FilePolicy:
public function view(User $user, File $file): bool
{
return $user->id === $file->user_id;
}
// In the controller:
$this->authorize('view', $file); // aborts with 403 if denied
Use authorize() (or Gate::authorize) for fine-grained checks beyond the auth middleware.
Step 5: Temporary Signed URLs (for expiring access)
For time-limited, shareable file links without storing auth state:
$url = URL::temporarySignedRoute(
'file.download',
now()->addMinutes(30),
['filename' => $filename]
);
The signed URL includes a hash that Laravel verifies; it expires after the given time. Add ->middleware('signed') to the route to validate it.
Summary of the Pattern
- Store files on the private disk (not
public/). - Serve through a route protected by the
authmiddleware. - In the controller, apply any per-resource authorization (policy/gate).
- Use
Storage::download()to stream the file - never expose the real path.
Why This Matters in Interviews
Laravel security interviews use this to test understanding of how files and authentication intersect. Key points: files in public/ are unprotected by definition - always store sensitive files on a private disk; the auth middleware is the gate; deliver with Storage::download(); and use Policies for per-user ownership checks. Temporary signed URLs for expiring access is a strong additional point.
Common Pitfalls
- Storing protected files in
public/storage(symlinked, directly accessible to anyone with the URL). - Using
Storage::url()on a private-disk file and sharing that URL (it doesn't enforce auth). - Forgetting authorization beyond authentication (another user's file ID in the URL).
- Exposing the real file-system path in the URL, enabling path traversal.
- Not handling
404when the file doesn't exist (exposes existence information).
References
- Laravel File Storage: https://laravel.com/docs/filesystem
- Laravel Middleware: https://laravel.com/docs/middleware
- Laravel Authorization (Policies): https://laravel.com/docs/authorization