Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Swaz's avatar
Level 20

Make Amazon S3 files private

I'm trying to learn Amazon S3.

When storing files locally, I know you can store them in storage/app/public, then make them publicly accessible with a sym link to public/storage.

And any files outside of storage/app/public will be private, and I can access them with an authenticated route that returns the file from the storage folder.

I was able to setup a Policy for my Bucket, so that only requests from my domain can access the files. But I think the files are still technically public.

  1. Is there a way to make the files private?
  2. Is there a need for this?
  3. How would I go about doing this?
  4. Should I leave them public and just obfuscate the path?

Any additional info on this would be great.

0 likes
7 replies
Swaz's avatar
Level 20

@spekkionu Thanks for the reply.

It looks like all files are set to private by default. I set one to public, but I don't really understand what that does. Is it just setting a flag so I can check to see if a given file is public or private?

deevo's avatar

From my understanding and experience, a private file cannot be accessed unless you have the permission to access that file. So, if you have an image on S3, you cannot just visit the .jpg image path to that file and see it. So, some random person could not just put that filepath in and view the image in their browser.

A public image that you have declared public will be able to be seen by anyone who can paste in the image path in their browser. Or, from a website, anyone can see that file inside of an img src tag.

I am not sure how you would have the ability to show an image in an application where from the application you can see the photo, but, from the outside world you cannot. I imagine it would involve some sort of authorization token that would have to go with the image url, or, you may have to actually get the file using your s3 credentials and then display that image.

Swaz's avatar
Level 20

@deevoweb Oh thanks, I see the difference now.

Displaying images on the web site:

// Works
<img src="{{ Storage::url('private.jpg') }}>

// Works
<img src="{{ Storage::url('public.jpg') }}>

Manually pasting the link into the browser:

// Works
https://s3-us-west-1.amazonaws.com/my-bucket/public.jpg

// Access denied
https://s3-us-west-1.amazonaws.com/my-bucket/private.jpg

I know that when storing files locally, you can use a route to return an image. Then do any authentication necessary like normal. But this doesn't work for images stored on S3.

<img src="/display-image">
Route::group(['middleware' => 'auth'], function () {

    Route::get('display-image', function () {
        return response()->file(storage_path('path/to/my/image.jpeg'));
    });
});

Edit:

Looks like this works, but only with images set to public:

Route::get('display-image', function () {

    $path = Storage::url('public.jpg');
    $image = Image::make(file_get_contents($path));

    return $image->response();
});

So maybe it's easiest to just obfuscate the path and link to the images like normal.

2 likes
spekkionu's avatar

When serving a file through a route it is better to use a stream rather than loading the entire contents of the file into memory.

$file = 'path/file.jpg';

return response()->stream(function() use($file) {
    $stream = Storage::readStream($file);
    fpassthru($stream);
    if (is_resource($stream)) {
        fclose($stream);
    }
}, 200, [
    "Content-Type" => Storage::mimeType($file),
    "Content-Length" => Storage::size($file),
    "Content-disposition" => "inline; filename=\"" . basename($file). "\"",
]); 

You can serve a private s3 file directly through the amazon server but it requires creating a pre-signed url for the file which Flysystem (the library Laravel uses for file handling) doesn't support so you would have to use the aws sdk directly.

https://docs.aws.amazon.com/aws-sdk-php/v3/guide/service/s3-presigned-url.html

3 likes
dmhamilt's avatar

@spekkionu I have followed the aws-sdk instructions and successfully created a preassigned url to a file. However when I use that url in a file path it fails. IF I use it as a hyper link it works.

 $s3Client = new \Aws\S3\S3Client([
            'region' => 'us-east-1',
            'version' => 'latest',
        ]);
        $cmd = $s3Client->getCommand('GetObject', [
            'Bucket' => 'example',
            'Key' => $file->path,
        ]);
        $request = $s3Client->createPresignedRequest($cmd, '+20 minutes');

        // Get the actual presigned-url
        $presignedUrl = (string)$request->getUri();

This works

       return '<a href='.$presignedUrl. ' > download </a>';

This fails stating file not found

        return response()->file($presignedUrl);

I would like to use the file or download feature because these are most likely not going to be view able files in the browser.

Update: Using the streaming above worked good enough. if it does not have the file type it automatically downloads.

Please or to participate in this conversation.