Laravel cookbook · Intermediate
Validate uploads and serve private files with temporary URLs
Uploads land on local disks, accept any file type, and private files are publicly accessible.
Approach
Validate type and size, store on a private disk (S3) under a generated name, save the path, and give users short-lived signed URLs after an authorization check.
Solution
<?php
namespace App\Http\Controllers;
use App\Models\Document;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
class DocumentController extends Controller
{
public function store(Request $request)
{
$request->validate([
'file' => ['required', File::types(['pdf', 'png', 'jpg'])->max(10 * 1024)], // KB
]);
$path = $request->file('file')->store("documents/{$request->user()->id}", 's3');
$document = $request->user()->documents()->create([
'path' => $path,
'original_name' => $request->file('file')->getClientOriginalName(),
]);
return response()->json(['id' => $document->id], 201);
}
public function download(Document $document)
{
Gate::authorize('view', $document);
return redirect(Storage::disk('s3')->temporaryUrl($document->path, now()->addMinutes(5), [
'ResponseContentDisposition' => 'attachment; filename="'.addslashes($document->original_name).'"',
]));
}
}
Pitfalls
- Storing uploads under
public/where anyone can guess URLs. - Trusting the client file name for the stored path.
- Large uploads through PHP when browsers could upload directly to S3 with presigned URLs.
Variations
- Use
Storage::disk('s3')->temporaryUploadUrl()for direct browser uploads. - Process images (resize, strip EXIF) in a queued job after upload.
Further reading: Temporary URLs
Reference: https://laravel.com/docs/13.x/filesystem#temporary-urls
More Laravel recipes
- Authorize actions with policies
- Cache expensive data with stale-while-revalidate
- Call external APIs with timeouts, retries and fakes
- Catch N+1 queries with strict Eloquent mode
- Delete old records automatically with prunable models
- Dispatch events and listeners after the transaction commits
- Issue scoped API tokens with Sanctum
- Make queued jobs reliable with retries, backoff and uniqueness