Honestly, there isn’t a secret 4th method hiding somewhere
— there are just a couple of proper, safe patterns, and everything else is basically a workaround with trade-offs.
The presigned / time-limited URL approach you mentioned is the one most people end up using, and for good reason. Generating the link in a backend workflow, after you’ve checked permissions, and letting it expire quickly is exactly how this is meant to be done. You’re not exposing admin keys, you’re not making the file permanently public, and you stay aligned with how S3 and similar storage systems are designed to work.
The idea of temporarily making a file public, converting it to base64, then deleting it isn’t automatically unsafe, but it’s definitely not ideal for anything sensitive. Even short windows can be cached, logged, or accessed in ways you didn’t expect. It works in a pinch, but I wouldn’t call it a clean or future-proof solution.
Appending a Bubble admin key to a file URL is, as you already know, a hard no. That’s basically putting the keys to your app into the wild.
If you want to go one step more secure than presigned links, the only real alternative is not exposing the file at all. That means serving or forwarding the file through a backend workflow (acting as a proxy): check permissions, fetch the file server-side, and return it or send it to the third-party service. It gives you full control, but it’s heavier and not always practical in Bubble, especially for large files.
So no magic 4th method — just:
-
presigned, short-lived URLs (best balance of security + simplicity), or
-
backend-only access if you need maximum control.
In most Bubble apps, I’d stick with backend-generated presigned URLs and keep the expiry short. That’s the approach that’s both safe and realistic.