← All field notes

Your Supabase Bucket Is Private. Your App Might Still Leak the Files.

A Supabase private bucket still needs correct application permissions. Check signed URLs, privileged reads, summaries and cross-customer file access.

Nov 4, 2026 · 5 min read


Field Notes from Thunkle, a studio that takes AI-built apps from prototype to secure, production-ready software.

A private Supabase bucket can stop an unauthenticated download and still leave a document exposed through your application. The bucket, its RLS policies and the server routes using privileged access need to enforce the same intended audience.

During an application review, I found a document-processing route that checked whether the caller was logged in. It did not check whether the requested document belonged to that caller. The backend could retrieve the file, so it retrieved it.

That finding concerned the application's authorization decision. The Supabase examples below explain the same failure pattern; they are not a claim that private buckets are inherently broken.

Review the route that reads the file

Consider a fictional document-summary feature. A customer signs in, supplies a document identifier and receives a summary. The server uses privileged access to retrieve the underlying file.

Authentication answers who the customer is. It does not answer whether that customer may process this document.

Before the read, the server should resolve a trusted document record and establish the caller's relationship to it. In a personal-document app, that might be ownership. In a workspace app, it could be current membership plus permission to run the operation.

Then derive the storage reference from that record. An arbitrary path supplied by the browser is not an authorization decision.

Supabase's storage access controls distinguish policy-controlled operations from privileged access. When your backend uses an elevated credential, the application must justify the operation for the caller.

Issuing a signed URL is a sensitive operation

A temporary link is useful when a customer needs limited access to a private object. The important check happens before the link is issued.

If customer A can obtain a valid link to customer B's private file, a short expiry does not fix that mistake. It only shortens the time the incorrect recipient can use it.

Check where links appear afterwards. Logs, support messages, emails and analytics events can have different audiences from the document itself. Do not include live signed links in public bug reports or routine telemetry.

Some products intentionally share files with anyone holding a link. That is a valid feature if its audience, duration and revocation behaviour match what the interface promises. It is different from private customer access.

Include the derived content

A PDF download can be blocked while its extracted text remains readable through a summary endpoint.

Follow the document through previews, OCR, text extraction, search indexing, generated answers, caches and exports. Each derivative needs an audience and a deletion policy.

A cache keyed only by a document name deserves investigation in a multi-customer app. It is not automatically a vulnerability: inspect the actual cache key, record identifiers and retrieval logic before claiming exposure.

The same applies to error messages. A failed processing job should not return fragments of another customer's document merely because the normal output was refused.

Test the operations separately

In an authorized test environment, create synthetic files for two unrelated accounts. Check the owner's normal workflow, then test the other account against the same operations.

Cover upload, preview, download-link creation, processing, replacement and deletion where those features exist. An upload policy passing does not prove that the parser or deletion route uses the same rule.

For shared workspaces, include an allowed colleague and a removed member. Check the response and the resulting state. A refused-looking page can still conceal a successful background action.

Record the operation and relationship in the evidence: “unrelated customer could not request a summary of this private document” is more useful than “storage passed”.

The two-account testing guide gives a repeatable starting point for the database side.

Permissions can change while a job waits

Suppose a member starts a long analysis and loses workspace access before it finishes. Should the result still be available to that person? What if the document is deleted or replaced while the job is queued?

Those are product decisions. Define them before treating the implementation's current behaviour as correct.

Review permission checks when work is accepted and when sensitive results are delivered. A job carrying an identifier that was valid yesterday does not establish current access.

A useful test starts the operation, changes the membership or document state, then checks the completed result with synthetic content. Also inspect stored outputs and relevant logs, not only the initial response.

Make sharing and revocation understandable

An owner may intend to share version one of a file, not every later replacement under the same object reference. Check whether links identify a version, a document or a mutable storage path.

If an issued link remains valid until expiry, the interface should not promise immediate revocation unless another control actually provides it. Retention in external recipients' systems is a separate limitation.

These details matter because “private” sounds like a complete promise to the customer. In practice, the app creates exceptions for processing, sharing and collaboration. Each exception needs a rule.

Fix the common decision, then retest each caller

If several endpoints repeat the same incomplete ownership check, patching the first reported route leaves the others uncertain. A shared authorization routine can help, provided operation-specific permissions remain explicit.

Retest both refused requests and legitimate customer actions. Tightening access until every request fails is not a successful repair.

For a Supabase security audit, I scope file access from the stored record through the features that return or modify its contents. The report should distinguish confirmed access failures, unresolved sharing decisions and paths that could not be tested. Remediation guidance and implementation are separate deliverables.

If you want to start with the checks yourself, read the free three-page security sample. It covers scanner scope and safe two-account setup, with no email required. For the full checklist, worked examples and coding prompts, get the Vibe Coding Security Launch Kit. The guide is a self-serve resource, not a review of your app.

Questions about private Supabase files

Does a private Supabase bucket protect a signed URL?

A private bucket does not correct a decision to issue a valid link to the wrong customer. Authorize the requester before creating the link, then check its intended audience and lifetime. Test derived outputs such as previews and summaries separately.

Should every file operation use the same policy?

It should follow the same product permission model, but different actions may require different permissions. Public reading of an image does not justify public replacement or deletion. Check each operation using permitted and unrelated test accounts.

Check who can read and change each customer file.

I can review the database relationship, storage rules and privileged routes behind your file workflow, then provide evidence and remediation guidance.