Cloud Storage CORS for Bubble: how to make browser uploads work without widening access

The short answer
CORS is the browser rule that decides whether JavaScript running on one origin may read a response from another origin. It is not the rule that decides whether a Bubble user belongs to the right customer, owns a file record, or may write to a Cloud Storage object. A browser-direct upload needs both layers: application authorization that grants one constrained upload, and cross-origin behavior that lets the approved browser request complete.
Start with the exact origins used by the app, the exact Cloud Storage endpoint, and the exact methods and headers sent by the upload client. Test the browser's preflight request before debugging credentials. Then test authorization separately. A permissive CORS response can make a request reachable without making it authorized; a restrictive or mismatched response can make a correctly authorized upload look broken.
This guide is conceptual. It explains a debugging and review method, not a claim that Bubble2X currently provides a managed upload workflow.
Why a valid upload can still fail in the browser
A Bubble page and a Cloud Storage endpoint normally have different origins. An origin is the combination of scheme, host, and port. https://app.example.com and https://storage.googleapis.com are different even though both use HTTPS. Browsers apply the same-origin policy to scripts, then use Cross-Origin Resource Sharing when the destination server explicitly permits the interaction.
Many upload requests are preflighted. Before sending the file, the browser sends an OPTIONS request describing the intended method and non-simple headers. The upload proceeds only when the response contains compatible CORS headers. A failure can therefore happen before Cloud Storage evaluates the eventual PUT or POST in the way you expected.
The browser console often reports these failures as a CORS error, but that message does not identify the root cause by itself. Common causes include:
the app ran from a different Bubble domain than the one configured;
the request used a different Cloud Storage endpoint family;
the method or requested header did not match the server response;
the resumable upload session was initiated without the browser's
Originheader;a redirect moved the request to an endpoint with different behavior;
the upload credential was invalid, but the error response lacked readable CORS headers.
Debug the network exchange, not only the final console sentence.
Inventory every Bubble origin
Bubble projects often have more than one legitimate origin. Development may use a bubbleapps.io address with a version-test path, while production uses a custom domain. A staging or preview setup may introduce another hostname. Ports matter during local testing, and HTTP and HTTPS are different origins.
Create an explicit environment table:
Environment | Page origin | Expected upload endpoint | Enabled? |
|---|---|---|---|
Bubble development | exact development origin | chosen Cloud Storage endpoint | only when needed |
Staging or preview | exact preview origin | chosen endpoint | time-bounded if temporary |
Production | exact custom-domain origin | chosen endpoint | yes |
Do not copy a wildcard into production simply to make the error disappear. Google Cloud documents that wildcard origins are supported in some configurations, but a broad browser allowance is not a substitute for a deliberate list. It can also make abuse easier when an upload credential leaks, because another site can attempt to use that credential from its own browser context.
At the same time, do not describe CORS as a security boundary stronger than it is. Non-browser clients do not have to obey browser CORS rules. Tenant authorization must still happen in a trusted service before an upload session or signed request is returned.
Choose the endpoint before writing the rule
Cloud Storage endpoint behavior is not uniform. Google's Cloud Storage CORS guide explains important differences between JSON API endpoints, XML API endpoints, and the authenticated browser download endpoint. For example, the storage.cloud.google.com authenticated browser download endpoint does not allow CORS requests. The same guide explains that JSON API endpoints return default CORS response values, while XML API endpoints evaluate the bucket's configured CORS rules.
That distinction affects both implementation and debugging. Record the literal endpoint shape generated by the upload flow. A signed URL commonly uses an XML API endpoint. A JSON API resumable upload uses an initiation request and then a returned session URI. Do not configure one endpoint family while testing another.
If a server-side component initiates a resumable session for a browser, Google's resumable upload documentation says to include the client's Origin header in that initiation request. Cloud Storage determines later CORS response headers from the initial request. Omitting the origin can cause subsequent cross-origin browser requests to fail even though the session URI itself is valid.
Map the request precisely
For one upload, capture the following without recording the credential value:
page origin;
initiation endpoint, if any;
upload endpoint or session host;
intended method, such as
PUTorPOST;request headers, including
Content-Type, integrity headers, and custom metadata headers;response headers the browser code needs to read;
whether credentials such as cookies are involved;
redirect behavior;
final completion-verification call.
Use the browser network panel to inspect the OPTIONS request and its response. Confirm that the response's allowed origin matches the page origin exactly, that the intended method is allowed, and that requested headers are accepted. Then inspect the actual upload request. A successful preflight proves only that the browser may attempt the request; it does not prove the storage operation will be authorized.
Avoid placing signed URLs or resumable session URIs in screenshots, logs, analytics, or support tickets. Google's documentation describes a resumable session URI as an authentication token. Redact the query string and any authorization material while retaining method, hostname, status, and safe header names.
Keep authorization ahead of CORS
A defensible browser-direct sequence looks like this:
Bubble sends an authenticated request containing a stable application file ID and safe metadata.
A trusted service resolves the current user and tenant from authenticated context.
The service verifies the user may create or replace this specific file record.
The service chooses the bucket and opaque object name from trusted configuration.
The service creates one constrained upload grant or resumable session.
The browser uploads directly to Cloud Storage using the approved origin flow.
The service verifies object existence, size, integrity, and policy before marking the file available.
Bubble's API Connector security guidance explains that sensitive connector parameters should be private so they remain server-side. If a browser must receive a temporary upload credential, treat that as an explicit exception with a narrow target and lifetime—not as permission to expose a long-lived service credential.
Bubble's Workflow API security guidance also separates authentication from the conditions and privacy rules that authorize work. An endpoint being reachable does not mean every authenticated user should be able to request any object name.
A useful CORS test matrix
Test both expected success and expected denial:
Test | Expected result |
|---|---|
Approved production origin, correct method and headers | Preflight and upload proceed |
Development origin not enabled | Browser blocks the cross-origin interaction |
Lookalike origin, such as a different subdomain | No matching CORS allowance |
Correct origin, unapproved method | Preflight does not authorize the method |
Correct origin, unexpected custom header | Preflight does not authorize the header |
Valid CORS, expired upload grant | Storage operation fails authorization |
Valid CORS, wrong tenant requests grant | Trusted service denies before issuing it |
Successful transfer, failed verification | File remains unavailable or enters review state |
Run the negative tests from a real browser because command-line tools do not enforce browser CORS rules. Separately use a command-line request when you need to determine whether the underlying storage credential works. The comparison is diagnostic: browser fails while command line succeeds often points toward CORS; both fail often points toward the request, credential, or endpoint.
Production checklist
Record every allowed origin and its owner.
Remove temporary preview origins when the review ends.
Confirm the endpoint family used in production.
Verify methods and headers from an observed request, not a remembered example.
Include the browser origin when initiating resumable sessions.
Redact signed URLs and session URIs from all evidence.
Deny wrong-tenant upload authorization before CORS is relevant.
Verify the completed object before showing it as available.
Monitor storage requests and application authorization failures separately.
Re-run the matrix after domain, proxy, upload-library, or header changes.
The design decision
If a direct browser upload is valuable because it keeps large file bytes off an application service, accept that CORS becomes an operational dependency. Make the configuration specific, observable, and environment-aware. If maintaining the browser interaction is too fragile for the team, a server-mediated upload may reduce browser complexity, but it moves bandwidth, streaming, timeout, and retry responsibility into the service.
The right outcome is not “CORS enabled.” It is a verified request path in which the correct Bubble user receives one constrained upload, the approved browser origin can complete it, unapproved origins and users fail, and the application marks the file ready only after server-side verification.
For the larger architecture, read How to upload files from Bubble to Google Cloud Storage securely and Bubble direct vs server-mediated uploads.
Primary keyword: Bubble Cloud Storage CORS
Primary CTA: Tell us which Bubble upload origin and workflow you need to support.
Sources checked: Cloud Storage CORS, Cloud Storage resumable uploads, Bubble API Connector security, and Bubble Workflow API security, checked 2026-10-02.
