GCS signed URL upload fails CORS: preflight OK, PUT rejected

Uploading to a GCS signed URL fails CORS: the preflight request completes fine, but the actual PUT is rejected with No 'Access-Control-Allow-Origin' header is present on the requested resource. The bucket CORS config is correct, which misleads debugging. The real cause: the Content-Type header sent with the upload did not match the content type baked into the signed URL, so GCS rejected the request before the CORS headers were applied.

When generating the signed URL, pass the exact Content-Type the client will upload with (e.g. image/png or application/octet-stream), and make sure the client sends that same header: users on the thread confirmed uploads work once the signed content-type matches. If you cannot know the type ahead of time, sign with application/octet-stream and upload as such.

Source: https://github.com/googleapis/nodejs-storage/issues/347