- How to Fix CORS Errors in Google Cloud Storage With gcloud CLI
How to Fix CORS Errors in Google Cloud Storage With gcloud CLI
A practical guide to updating CORS configurations, solving IAM and service account issues, and getting your frontend talking to GCS smoothly.

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:
[
{
"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:
gcloud storage buckets update gs://your-bucket-name --cors-file=cors-config.json
In my case:
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
- Go to Google Cloud Console
- Navigate to IAM & Admin > IAM
- Find your account and click "Edit"
- Add the Storage Admin role
- Save changes
Solution 2: Use gcloud to grant permissions
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:
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:
# 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:
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:
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:
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
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:
- In Google Cloud Console, go to your bucket
- Click on the Permissions tab
- Switch to Fine-grained: Object-level ACLs enabled
Enabling Public Access (If Needed)
If your files need to be publicly accessible:
- In the bucket permissions, click Add members
- Add
allUsers
as a member - 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:
[
{
"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:
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:
- CORS configuration itself is straightforward - it's a simple JSON file and one gcloud command
- Permissions are the real challenge - ensure you have
storage.buckets.update
permission - Authentication matters - know whether you're using your personal account or service account
- Double-check the basics - bucket names, project IDs, and exact origins
- 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