---
name: gcs-cors-signed-url-get
description: GCS CORS rules must explicitly list GET method for browser fetch of signed URLs — omitting it causes CORS preflight failure even though the signed URL is valid
type: concept
---

# GCS CORS — Signed URL Requires Explicit GET Method

## The Problem

A browser `fetch()` of a GCS signed URL fails with CORS error, but:
- The signed URL is valid (curl works)
- The bucket CORS config exists
- The bucket is not public

Root cause: CORS config does not explicitly include `GET` in the allowed methods.

## Why Signed URLs Still Need CORS

Signed URLs authenticate the *request* via the URL signature, but CORS is a browser policy enforced separately. Even a perfectly signed URL triggers a CORS preflight (OPTIONS) when fetched from JavaScript — and the browser checks the CORS headers in the response, independent of the signature.

## The Fix

Set the GCS bucket CORS config to include `GET` (and `HEAD` for range requests):

```json
[
  {
    "origin": ["https://your-app.com", "http://localhost:5173"],
    "method": ["GET", "HEAD"],
    "responseHeader": ["Content-Type", "Content-Range", "Accept-Ranges"],
    "maxAgeSeconds": 3600
  }
]
```

Apply via `gsutil`:

```bash
gsutil cors set cors.json gs://your-bucket-name
gsutil cors get gs://your-bucket-name  # verify
```

Or via Terraform:

```hcl
resource "google_storage_bucket_iam_member" "..." { ... }

resource "google_storage_bucket" "bucket" {
  cors {
    origin          = ["https://your-app.com"]
    method          = ["GET", "HEAD"]
    response_header = ["Content-Type"]
    max_age_seconds = 3600
  }
}
```

## Checklist When Browser Fetch Fails

1. Confirm signed URL works in curl → confirms auth is fine
2. Check bucket CORS with `gsutil cors get` → look for missing methods
3. Confirm `origin` in CORS includes the browser's origin exactly (no trailing slash)
4. Check `responseHeader` if the app reads custom response headers

## Related

- [[wiki/concepts/gcs-resumable-upload-pattern]] — browser→GCS via resumable session URI

**Source:** daily/2026-05-13.md | 2026-05-13
