---
title: "How to Fix CORS Errors in Google Cloud Storage With gcloud CLI"
slug: "fix-cors-errors-google-cloud-storage-gcloud-cli"
published: "2025-08-01"
updated: "2025-12-26"
validated: "2025-10-20"
tags:
  - "google cloud storage cors"
  - "gcloud cli"
  - "cors configuration"
  - "gcs cors fix"
  - "bucket cors"
  - "cloud storage security"
  - "cors policy"
  - "gcloud cors"
  - "storage access"
  - "cors headers"
llm-intent: "reference"
framework-versions:
  - "gcloud CLI@latest"
  - "Google Cloud Storage API v1"
  - "gsutil@4.x"
  - "React@18"
  - "Node.js@20"
status: "stable"
llm-purpose: "Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast."
llm-prereqs:
  - "General familiarity with the article topic"
llm-outputs:
  - "Completed outcome: Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast."
---

**Summary Triples**
- (CORS configuration, is applied with, gcloud storage buckets update BUCKET_NAME --cors-file=cors.json)
- (cors.json, must include, origin, method, responseHeader, maxAgeSeconds fields (example array with allowed origins and methods))
- (Permission error 'storage.buckets.update: Permission denied', indicates, active account lacks storage.buckets.update permission; grant roles/storage.admin or appropriate role to the account)
- (Service account authentication, requires, gcloud auth activate-service-account --key-file=KEY.json and the service account granted storage role(s))
- (Bucket access control, has modes, Uniform (IAM-only) and Fine-grained (ACLs + IAM); uniform disables ACLs and relies solely on IAM roles)
- (Verify CORS, use, gcloud storage buckets describe BUCKET_NAME or send an OPTIONS request with Origin header to check Access-Control-Allow-Origin)
- (Common root causes, include, wrong bucket name, wrong active account, missing IAM role, uniform bucket restrictions, or stale credentials)
- (Local dev origin, must be, added to cors.json (e.g., http://localhost:3000) for browser requests to succeed in development)

### {GOAL}
Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast.

### {PREREQS}
- General familiarity with the article topic

### {STEPS}
1. Follow the detailed walkthrough in the article content below.

<!-- llm:goal="Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast." -->
<!-- llm:prereq="General familiarity with the article topic" -->
<!-- llm:output="Completed outcome: Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast." -->

# How to Fix CORS Errors in Google Cloud Storage With gcloud CLI
> Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast.
Matija Žiberna · 2025-08-01

While building a construction management app that needed to handle file uploads and downloads from a React frontend, I ran into one of those seemingly simple tasks that turned into a multi-hour debugging session. I had a Google Cloud Storage bucket set up for storing project recordings, presigned URLs working in my backend, but every time my localhost:3000 development server tried to make requests to the bucket, I'd get hit with CORS errors.

"Easy fix," I thought. "Just add a CORS configuration to the bucket." But what started as a quick `gcloud` command quickly spiraled into a permissions maze involving service accounts, IAM roles, bucket-level permissions, and the subtle differences between uniform and fine-grained access control.

After wrestling with `storage.buckets.update` permission denials, switching between personal and service accounts, and discovering that my bucket name had a typo that I'd been living with for months, I finally got everything working. The solution involved not just the CORS configuration itself, but understanding GCS permissions, authentication flows, and access control models.

## What You'll Learn

By the end of this article, you'll know how to:
- Create and apply CORS configurations to Google Cloud Storage buckets using `gcloud`
- Troubleshoot common permission issues with service accounts and user authentication
- Understand the difference between uniform and fine-grained access control
- Test and verify your CORS configuration is working properly
- Handle authentication switching between personal and service accounts

If you're dealing with CORS issues between your web app and Google Cloud Storage, or if you've ever stared at a "does not have permission to access" error while knowing you're definitely the project owner, this guide will walk you through the complete setup process – including all the permission gotchas I encountered along the way.

## Prerequisites

Before we start, make sure you have:
- `gcloud` CLI installed and configured
- A Google Cloud Storage bucket created
- Basic understanding of CORS concepts
- Access to your Google Cloud project (we'll handle permission issues as they come up)

## Step 1: Understanding CORS Configuration

CORS (Cross-Origin Resource Sharing) allows web applications running on one domain to access resources from another domain. For Google Cloud Storage, this means configuring your bucket to accept requests from your web application's origin.

A CORS configuration file is a JSON array containing rules that specify:
- **Origins**: Which domains can access your bucket
- **Methods**: Which HTTP methods are allowed (GET, POST, PUT, DELETE, OPTIONS)
- **Response Headers**: Which headers the browser can access
- **Max Age**: How long browsers should cache the CORS preflight response

## Step 2: Create Your CORS Configuration File

Create a file named `cors-config.json` with the following content:

```json
[
  {
    "origin": ["http://localhost:3000"],
    "method": ["GET", "POST", "OPTIONS"],
    "responseHeader": [
      "Content-Type",
      "Content-Range",
      "Content-Length",
      "Content-Disposition",
      "ETag",
      "Cache-Control"
    ],
    "maxAgeSeconds": 3600
  }
]
```

**Key points about this configuration:**
- `OPTIONS` method is crucial for preflight requests
- We include standard headers needed for file operations
- `maxAgeSeconds: 3600` caches the preflight response for 1 hour
- Using `http://localhost:3000` for local development (adjust as needed)

## Step 3: Apply the CORS Configuration

This is where things got interesting for me. The command itself is simple:

```bash
gcloud storage buckets update gs://your-bucket-name --cors-file=cors-config.json
```

In my case:
```bash
gcloud storage buckets update gs://recorings --cors-file=cors-config.json
```

But this is where I hit my first roadblock...

## Troubleshooting Permission Issues

### The First Error: User Account Permissions

```
ERROR: [matija@we-hate-copy-pasting.com] does not have permission to access b instance [recorings] (or it may not exist): matija@we-hate-copy-pasting.com does not have storage.buckets.update access to the Google Cloud Storage bucket.
```

Even though I thought I had the right permissions, my user account lacked the `storage.buckets.update` permission. Here's how I fixed it:

**Solution 1: Grant yourself Storage Admin role**
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Navigate to **IAM & Admin > IAM**
3. Find your account and click "Edit"
4. Add the **Storage Admin** role
5. Save changes

**Solution 2: Use gcloud to grant permissions**
```bash
gcloud projects add-iam-policy-binding your-project-id \
    --member="user:your-email@domain.com" \
    --role="roles/storage.admin"
```

### The Second Error: Service Account Permissions

I had a service account for my application, so I tried using that instead:

```bash
gcloud auth activate-service-account --key-file=/path/to/service-account.json
```

But got the same permission error for the service account. The solution was to grant the service account the necessary permissions:

```bash
# Switch back to your personal account
gcloud auth login your-email@domain.com

# Grant permissions to service account
gcloud projects add-iam-policy-binding your-project-id \
    --member="serviceAccount:service-account@project.iam.gserviceaccount.com" \
    --role="roles/storage.admin"

# Switch back to service account
gcloud auth activate-service-account --key-file=/path/to/service-account.json
```

## Step 4: Verify Your Configuration

Once the command succeeds, verify your CORS configuration:

```bash
gcloud storage buckets describe gs://your-bucket-name --format="value(cors)"
```

This should output your CORS configuration in JSON format. You can also view the full bucket description:

```bash
gcloud storage buckets describe gs://your-bucket-name
```

Look for the `cors:` section in the output.

## Step 5: Test Your CORS Configuration

### Browser Console Test
Open your browser's developer console on `http://localhost:3000` and run:

```javascript
fetch('https://storage.googleapis.com/your-bucket-name/test-file', {
  method: 'GET',
  mode: 'cors'
})
.then(response => console.log('CORS working:', response.status))
.catch(error => console.log('CORS error:', error));
```

### cURL Test for Preflight
```bash
curl -H "Origin: http://localhost:3000" \
     -H "Access-Control-Request-Method: GET" \
     -H "Access-Control-Request-Headers: Content-Type" \
     -X OPTIONS \
     https://storage.googleapis.com/your-bucket-name/
```

Look for `Access-Control-Allow-Origin` headers in the response.

## Additional Configuration: Access Control Models

During my setup, I also switched my bucket to use fine-grained access control and enabled public access via ACL. Here's why and how:

### Switching to Fine-Grained Access Control

Google Cloud Storage offers two access control models:
- **Uniform bucket-level access**: Simpler, uses only IAM
- **Fine-grained access**: Allows both IAM and Access Control Lists (ACLs)

I switched to fine-grained because I needed more granular control over individual objects:

1. In Google Cloud Console, go to your bucket
2. Click on the **Permissions** tab
3. Switch to **Fine-grained: Object-level ACLs enabled**

### Enabling Public Access (If Needed)

If your files need to be publicly accessible:

1. In the bucket permissions, click **Add members**
2. Add `allUsers` as a member
3. Assign the **Storage Object Viewer** role

**Warning**: Only do this if your files should be publicly accessible!



## Adding Production Domains Later

When you're ready to deploy to production, simply update your CORS configuration:

```json
[
  {
    "origin": [
      "http://localhost:3000",
      "https://yourproductiondomain.com"
    ],
    "method": ["GET", "POST", "OPTIONS"],
    "responseHeader": [
      "Content-Type",
      "Content-Range",
      "Content-Length",
      "Content-Disposition",
      "ETag",
      "Cache-Control"
    ],
    "maxAgeSeconds": 3600
  }
]
```

Then apply the updated configuration:

```bash
gcloud storage buckets update gs://your-bucket-name --cors-file=cors-config.json
```

## Conclusion

What started as a simple CORS configuration turned into a comprehensive lesson in Google Cloud Storage permissions, authentication, and access control models. The key takeaways are:

1. **CORS configuration itself is straightforward** - it's a simple JSON file and one gcloud command
2. **Permissions are the real challenge** - ensure you have `storage.buckets.update` permission
3. **Authentication matters** - know whether you're using your personal account or service account
4. **Double-check the basics** - bucket names, project IDs, and exact origins
5. **IAM changes take time** - be patient with permission propagation

The `gcloud storage buckets update --cors-file` command is powerful and flexible, but make sure you have your authentication and permissions sorted first. Once you understand the permission model, updating CORS configurations becomes a breeze.

Remember to test your CORS configuration thoroughly in both development and production environments, and don't forget to include the OPTIONS method for preflight requests. With the right setup, your web application will seamlessly communicate with Google Cloud Storage without any CORS headaches.

Thanks,
Matija

## LLM Response Snippet
```json
{
  "goal": "Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast.",
  "responses": [
    {
      "question": "What does the article \"How to Fix CORS Errors in Google Cloud Storage With gcloud CLI\" cover?",
      "answer": "Fix CORS errors in Google Cloud Storage using gcloud CLI. Learn to update CORS configs and solve common permission and authentication issues fast."
    }
  ]
}
```