What's the cleanest, most secure way to send a private file to OpenAI or other 3rd party service?

I’ve seen a few different methods proposed:

  1. Create a temporary public file, convert it to base64, then delete it (this seems potentially not secure).

  2. Attach a Bubble admin key to the end of the file URL (definitely not doing this).

  3. Create a temporary presigned link in the backend using this plugin (I’m leaning towards this one as it seems the safest).

Is there a secret 4th method?

Your third option is the right approach. Generating a pre signed link in a backend workflow is the standard, secure method for granting temporary access to a private file. This avoids making the file public or exposing any long-term credentials.

1 Like

I also think the best approach would be to use an external service to generate a short-lived presigned URL through a backend workflow. That would be the most secure way.

The file remains private, access is time-limited, and no permanent credentials or public URL are exposed, which is exactly what @randomanon want when sending files to OpenAI.

Do you mean like Wasabi or S3 directly? It’s the same concept right?

Yes! Exactly!

I’ve never used Wasabi, only S3, but with either one, you’ll be able to achieve the same result.

I found these two links here:

Any advantage to doing this compared to just doing the same thing from Bubble?

In Bubble, you cannot create a “presigned URL” with this temporary access. You can only choose whether to make a file private or not.

My understanding is that the plugin does effectively that. Creates a presigned URL that’s active for a few minutes.

Good observation, and I apologize for not analyzing the plugin more deeply at the beginning. I knew that Bubble doesn’t allow generating presigned URLs in a flexible way, and I also wasn’t aware that it was possible to force this behavior.

So I stopped to test and understand the plugin in more depth. What it does is not generate a presigned URL by itself (it doesn’t sign anything or control expiration), but instead forces access to a private Bubble file (via your app’s token/API key), which causes Bubble to automatically generate a temporary presigned URL and redirect to it.

So in practice, yes, you end up with a presigned URL, but the signing is done by Bubble, not by the plugin. The plugin simply resolves and returns the redirect.

This also means there are limitations: no control over the expiration time (I tested it and the expiration was around 10 hours), no per-user permissions, and no way to revoke access — everything is managed by Bubble. For more control, using third-party storage (S3/Wasabi) is still the best option.

1 Like

Honestly, there isn’t a secret 4th method hiding somewhere :slightly_smiling_face: — 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.

GPT hallucination.

All of your posts are copy and pasted from chatGPT. Please stop or you will be banned.

2 Likes