NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Reviews for Laravel

Private photos & storage

Set photos.visibility to private and every photo is served through short-lived signed URLs only — never a public one:

REVIEWS_PHOTOS_VISIBILITY=private
REVIEWS_PHOTOS_PRIVATE_DISK=local

# Or pin every photo to one explicit, non-public disk:
REVIEWS_PHOTOS_DISK=s3-private

Every reader resolves each photo by its own visibility. Public photos get their public (CDN-able) URLs; private photos get signed URLs — presigned on disks that support it, otherwise Media Library’s signed streaming route. That covers firstPhotoUrl(), photoUrls(), resolvePhotoUrl(), photoSrcset(), responsivePhotos() and ReviewResource:

// With photos.visibility = private, every reader returns short-lived signed URLs.
$review->firstPhotoUrl();                  // signed, never a public URL
$review->photoUrls();                      // each one signed
$review->responsivePhotos();               // <img> tags whose src and srcset are signed

// Choose your own expiry for one link (variant, expiry):
$review->firstPhotoTemporaryUrl('', now()->addMinutes(30));

Signed links default to Media Library’s media.temporary_url_default_lifetime (5 minutes). responsivePhotos() renders the same <img> tag Media Library would — smallest generated width as src, every width in srcset, sizes, alt, class and the blur-up placeholder — with signed URLs throughout.

Where photos are stored

photos.diskphotos.visibilityOriginalsVariants
setanyphotos.diskmedia.variants_disk if set, otherwise photos.disk
nullprivatephotos.private_disk (local)photos.private_disk — always, whatever media.variants_disk says
nullpublicmedia-library’s default disk (media.disk)media.variants_disk if set, otherwise media.disk

Private photos never land on Media Library’s default public disk, where anything is reachable under /storage once storage:link runs — the signed URL is the only way in. If you set photos.disk explicitly, it is used for every original, so keep it non-public while photos are private — and if you also set media.variants_disk, make sure that disk is non-public too, because variants follow it.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.