Short answer: keep the bucket private, put a short-lived signed URL behind an authorization check, and enforce deletion with a lifecycle worker that records every decision. The cheapest design is the one that deletes the right bytes on time; a low storage rate does not rescue a retention process...
Source: [Dev.to](https://dev.to/owensullivan9135/private-image-storage-signed-download-links-retention-and-object-store-trade-offs-461i)