Laravel: Upload File and Hide Real URL for Secure Download under UUID

File uploads are one of the essential things in web apps these days. But secure download of these files is sometimes even more important. So how to store files securely so people wouldn’t have access to them or guess their URLs or IDs of their records? Here’s a small demo tutorial.

What we’re building here

A small list of books with their covers, looks like this:

Looks simple, right? Now, let’s add some conditions:

  • Original cover filename should be visible but file can’t be accessed in public directly
  • You can download book cover with URL with book ID as a parameter, but that ID cannot be “guessable”

Preparation: structure

We will deal with only one DB table.
This is how our database migration looks like:

public function up()
    Schema::create('books', function (Blueprint $table) {

Field uuid will be used to hide original ID, and field cover will store the original filename.

Model app/Book.php is really simple – only fillable fields:

class Book extends Model
    protected $fillable = ['uuid', 'title', 'cover'];

Now, we need two routes in routes/web.php:

Route::resource('books', 'BookController');
Route::get('books/{uuid}/download', 'BookController@download')->name('');

So we manage books with resourceful BookController and will have a separate route for download.
The first part of controller looks simple – list and create form:

use App\Book;

class BookController extends Controller
    public function index()
        $books = Book::all();
        return view('books.index', compact('books'));

    public function create()
        return view('books.create');

This is how our views look like – just a simple table and form.



<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Books list</div>

                <div class="card-body">

                    <a href="{{ route('books.create') }}" class="btn btn-primary">Add new book</a>
                    <br /><br />

                    <table class="table">
                            <th>Download file</th>
                        @forelse ($books as $book)
                                <td>{{ $book->title }}</td>
                                <td><a href="{{ route('', $book->uuid) }}">{{ $book->cover }}</a></td>
                                <td colspan="2">No books found.</td>


And resources/views/books/create.blade.php:


<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Add new book</div>

                <div class="card-body">

                    <form action="{{ route('') }}" method="POST" enctype="multipart/form-data">

                        <input type="text" name="title" class="form-control">


                        Cover File:
                        <input type="file" name="cover">


                        <input type="submit" value=" Upload book " class="btn btn-primary">



Notice: our main resources/views/layouts/app.blade.php file is generated with php artisan make:auth command, we’re just reusing the same structure as default Laravel login/register pages.

This is how simple is the upload form:

Uploading the file

I have written a big big guide about uploading files in Laravel, but for this tutorial you need to know only a few things:

1. Upload form should contain <form enctype=”multipart/form-data”> – I’m shocked how many people forget this.

2. Upload app/Http/BookController.php code will look like this:

use Webpatser\Uuid\Uuid;

public function store(Request $request)
    $book = $request->all();
    $book['uuid'] = (string)Uuid::generate();
    if ($request->hasFile('cover')) {
        $book['cover'] = $request->cover->getClientOriginalName();
        $request->cover->storeAs('books', $book['cover']);
    return redirect()->route('books.index');

What we’re doing here:

  • We build the array $book which then is passed to Book::create() method;
  • We generate a unique UUID for the book with the help of webpatser/laravel-uuid package;
  • We check if there is a file, if so – we upload it to a storage/app/books folder with original filename (actual folder may depend on your config/filesystems.php settings);
  • Finally we redirect back to the list.

As a result, here’s what file will be uploaded and in which folder:

And here’s our database entry:

By default, file is stored in storage/app folder and not accessible to the public (unless you choose public driver), so that’s good – we achieve our first goal of securing the file.

But the second part – how to download the file without ability to guess its filename or ID?

Downloading File by its UUID

We already have this route:

Route::get('books/{uuid}/download', 'BookController@download')->name('');

In our Blade file, we refer to this route with this syntax:

<a href="{{ route('', $book->uuid) }}">{{ $book->cover }}</a>

So, this is how the actual URL looks like:

Finally, this is how download method looks like in app/Http/Controllers/BookController.php:

public function download($uuid)
    $book = Book::where('uuid', $uuid)->firstOrFail();
    $pathToFile = storage_path('app/books/' . $book->cover);
    return response()->download($pathToFile);

That’s it, users can download the file with its original filename, but without knowing where it is stored on the server, or book ID.

Hope that was helpful!

Like our articles?
Check out our Laravel online courses!


  1. Why you don’t use the UUID as the file name for storing it ? If two files with the same name are uploaded, the second one will destroy the previous one.
    Many thanks for your briliant posts and videos.

    • Good point, Gilles, this article indeed doesn’t cover that case.
      Or, probably, we should store files in their UUID folders, leaving original name. That depends on your implementation.

      Thanks for the comment!

  2. Hi there,
    Thanks for this very nice article.
    i am using Laravel 5.8.34 and having many problems issues following this tutorial. Could you please let me know if this is because of the Laravel version or something else?
    Many thanks

      • Thanks for getting back so quickly. Got few issues.
        1) Not sure why it is throwing an error
        Class’App\Book’ not found?
        Not sure if I made right moves towards the end of the tutorial as this feels like a high level tutorials where you skipped some basic steps considering one should know. Actually, I found it very interesting but can’t make it complete?
        I do understand the logic and reason of this tutorial but struggling with issus. Anychane if you have this on the github i can fork it from there?
        Many thanks for your prompt reply and much appreciated for this very nice tutorial.

        • I can’t know which part of the code you missed and not sure why model is missing for you. And sorry I don’t have it on github. I feel that I explained it pretty well with enough details, but I guess for next articles I should always include github repository for people to actually try out.

          • GITHUB would be excellent. Do you have a youtube channel. People like you should be there too! It’s always great help. Any chance I can have your email address, please?

  3. Brother. I want to show video in video tag. and I want to make this system that anyone could not download video from coping the source link of video

  4. very best!
    I set up an online training site and fully implemented the download system and it works. The URL of the images and files is as follows:
    I just got a download host and now my addresses should look like this
    1- My question is how can I connect my Laraveli project to the download host
    2- The second question is how can I read download files such as pictures and video of courses after connecting Laravel to the host?
    3- How to send them to the download host when uploading the file? Thank you for your detailed answer


Please enter your comment!
Please enter your name here