Skip to main content

Firebase Storage: Project cover images

Project cover images are uploaded via the app’s API (server-side) to avoid CORS. The browser sends the file to POST /api/projects/[projectKey]/upload-image; the server uploads to the bucket and returns a signed URL. No CORS configuration is required for uploads.

Images are stored at:

project-images/{projectKey}/{timestamp}-{filename}

CORS (required for uploads from browser)

If you see CORS errors in the console when uploading (e.g. from http://localhost:3000), the Storage bucket must allow your origin. Firebase Storage does not allow cross-origin requests by default.

One-time setup with gsutil

  1. Install Google Cloud SDK (includes gsutil): https://cloud.google.com/sdk/docs/install

  2. Authenticate and set project:

    gcloud auth login
    gcloud config set project gtc-tools-dev
  3. Find your Storage bucket name. The Firebase Console shows a URL like gtc-tools-dev.firebasestorage.app, but the GCS bucket name is often different. List buckets:

    gsutil ls

    Use the bucket that matches your project (commonly gs://gtc-tools-dev.appspot.com). You can also copy the bucket name from Firebase Console → Storage → Files (bucket is shown at the top).

  4. Apply CORS (replace BUCKET_NAME with the bucket from step 3). The app uses gtc-tools-dev.appspot.com by default, so CORS must be set on that bucket:

    gsutil cors set apps/web/firebase-storage.cors.json gs://BUCKET_NAME

    Example:

    gsutil cors set apps/web/firebase-storage.cors.json gs://gtc-tools-dev.appspot.com
  5. Add production origin to apps/web/firebase-storage.cors.json if you host on a different domain, then run the gsutil cors set command again.

After this, uploads and image loads from the configured origins will work.


Storage rules

Ensure your Firebase Storage rules allow authenticated users to read and write under project-images/. Example (Firebase Console → Storage → Rules):

rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /project-images/{projectKey}/{allPaths=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}
  • read: public so project cards can show images without auth.
  • write: only authenticated users can upload (prevents anonymous uploads).

Adjust to match your security requirements (e.g. restrict read to authenticated users if needed).