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-privateEvery 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.disk | photos.visibility | Originals | Variants |
|---|---|---|---|
| set | any | photos.disk | media.variants_disk if set, otherwise photos.disk |
| null | private | photos.private_disk (local) | photos.private_disk — always, whatever media.variants_disk says |
| null | public | media-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 cryptoBy 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.