Short answer: Use an application proxy when every request needs application-level authorization, signed URLs when a time-bounded bearer capability is acceptable, and edge authorization when you need both access checks and CDN delivery. Some transformed derivatives may be safely public even when originals are private, but that must be an explicit policy.
There is no single definition of a private image. A customer invoice scan, a draft campaign asset, a paid publication image, and a profile photo each have different access, sharing, revocation, and caching needs. Start with the threat model and lifecycle, not the delivery feature name.
Pattern 1: proxy every request through the application
The browser requests an application URL with its normal session. The application authorizes the user, retrieves or streams the image, and returns it. This provides immediate business-rule enforcement and can hide storage details completely.
The tradeoff is load. Your application handles image bandwidth and long responses unless a protected acceleration layer is added. Poorly implemented proxies buffer entire files, tie up workers, omit range support, and become a reliability bottleneck.
Use streaming, strict upstream limits, correct content headers, and a cache policy consistent with the data. Do not allow an arbitrary upstream URL parameter or the proxy becomes an SSRF surface. The origin protection guide applies here.
Pattern 2: issue expiring signed URLs
After authenticating the user, the application returns a URL containing a verifiable signature and expiry. The browser retrieves the image directly from the media service or CDN. This offloads transfer and works well when temporary bearer access matches the product.
Anyone who obtains the URL can generally use it until expiry unless the token is bound to another property. Short lifetimes improve revocation but can break long sessions and caching. Long lifetimes improve reliability but extend exposure.
Cloudflare documents this model for serving private images. Cloudinary describes authenticated and private delivery types in its control access documentation. Behavior and cache semantics differ, so test your provider.
The signed image URL guide explains what to sign and where keys belong.
Pattern 3: authorize at the edge
An edge function or CDN authorization feature validates a cookie, token, or application-issued assertion before allowing a cached object to be returned. This can preserve low-latency delivery while applying an access decision on every request.
The cache must not bypass authorization. Validate before cache delivery or use a product feature explicitly designed for protected caching. Avoid varying the stored image by every user identity when the bytes are shared among an authorized group; validate the user and then map to stable content identity.
Edge authorization adds another runtime and deployment surface. Keep token verification narrow, use protected secret storage or asymmetric verification, and define failure behavior if identity services are unavailable.
Pattern 4: keep originals private and publish selected derivatives
A site may hold an original privately while intentionally publishing a watermarked, low-resolution, or approved crop. This works when the derivative itself is not sensitive. It does not protect the derivative merely because the source is private.
Make publication a recorded state transition. Use distinct delivery identifiers or namespaces so a public cache cannot accidentally serve a private variant. Prevent arbitrary transformations from a public derivative back to a larger or unwatermarked result.
How should caching be designed?
Decide whether the response is user-specific, group-shared, or identical for everyone who passes authorization. Set browser and shared-cache directives accordingly. A private cache directive concerns shared cache storage; it does not authenticate the requester.
Separate the authorization token from the content key where the platform safely supports it. Otherwise, unique signatures can fragment the cache. But never remove a token from cache identity if doing so also removes the access check.
Test a warm object with a valid token, expired token, missing token, and a token for another asset. Then repeat from another account and region. Review the image cache key guide before making query exclusions.
What about revocation and logging?
Expiry is not immediate revocation. For urgent removal, rotate an asset version, disable its delivery ID, update the authorization policy, or purge protected caches according to provider capabilities. Document which action reaches which layers.
Log access decisions with asset ID, policy, result, and trusted principal identifier where required. Do not log full bearer tokens. Apply retention and privacy controls to logs because image access can itself reveal sensitive behavior.
Choose the simplest pattern that satisfies the real access model. Public content should stay simple. Sensitive content needs explicit authorization boundaries, cache tests, revocation procedures, and observability that proves the boundary is working.

Leave a Reply