Configuring cross-origin resource sharing (CORS)

Go to concepts

Cross Origin Resource Sharing (CORS) is a mechanism for allowing interactions between resources from different origins, something that is normally prohibited in order to prevent malicious behavior. Use this topic to learn how to configure CORS on a Cloud Storage bucket.

Configuring CORS on a bucket

You set a CORS configuration on a bucket by specifying information, such as HTTP methods and originating domains, that identify the types of requests it will accept. You can use the gsutil command-line tool, the XML API, the JSON API, or the client libraries for Cloud Storage to set CORS configuration on a bucket.

The following example illustrates how to configure CORS on your bucket. The example sets the CORS configuration as follows:

  • Allow requests that originate from example.appspot.com.
  • Allow requests that use the GET HTTP method.
  • Allow the Content-Type response header to be shared across origins.
  • For preflighted requests, allow the browser to make requests for 3600 seconds (1 hour) before it must repeat the preflight request.

gsutil

  1. Create a JSON file that contains the following:

    [
        {
          "origin": ["http://example.appspot.com"],
          "responseHeader": ["Content-Type"],
          "method": ["GET"],
          "maxAgeSeconds": 3600
        }
    ]
  2. Use the gsutil cors command to configure CORS on a bucket:

    gsutil cors set [JSON_FILE_NAME].json gs://[BUCKET_NAME]

    Where

    • [JSON_FILE_NAME] is the path to the JSON file you created in Step 1.
    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.

Code samples

C++

For more information, see the Cloud Storage C++ API reference documentation.

namespace gcs = google::cloud::storage;
using ::google::cloud::StatusOr;
[](gcs::Client client, std::string const& bucket_name,
   std::string const& origin) {
  StatusOr<gcs::BucketMetadata> original =
      client.GetBucketMetadata(bucket_name);

  if (!original) throw std::runtime_error(original.status().message());
  std::vector<gcs::CorsEntry> cors_configuration;
  cors_configuration.emplace_back(
      gcs::CorsEntry{3600, {"GET"}, {origin}, {"Content-Type"}});

  StatusOr<gcs::BucketMetadata> patched_metadata = client.PatchBucket(
      bucket_name,
      gcs::BucketMetadataPatchBuilder().SetCors(cors_configuration),
      gcs::IfMetagenerationMatch(original->metageneration()));

  if (!patched_metadata) {
    throw std::runtime_error(patched_metadata.status().message());
  }

  if (patched_metadata->cors().empty()) {
    std::cout << "Cors configuration is not set for bucket "
              << patched_metadata->name() << "\n";
    return;
  }

  std::cout << "Cors configuration successfully set for bucket "
            << patched_metadata->name() << "\nNew cors configuration: ";
  for (auto const& cors_entry : patched_metadata->cors()) {
    std::cout << "\n  " << cors_entry << "\n";
  }
}

Java

For more information, see the Cloud Storage Java API reference documentation.

import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Cors;
import com.google.cloud.storage.HttpMethod;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.google.common.collect.ImmutableList;

public class ConfigureBucketCors {
  public static void configureBucketCors(
      String projectId,
      String bucketName,
      String origin,
      String responseHeader,
      Integer maxAgeSeconds) {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The origin for this CORS config to allow requests from
    // String origin = "http://example.appspot.com";

    // The response header to share across origins
    // String responseHeader = "Content-Type";

    // The maximum amount of time the browser can make requests before it must repeat preflighted
    // requests
    // Integer maxAgeSeconds = 3600;

    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    Bucket bucket = storage.get(bucketName);

    // See the HttpMethod documentation for other HTTP methods available:
    // https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/urlfetch/HTTPMethod
    HttpMethod method = HttpMethod.GET;

    Cors cors =
        Cors.newBuilder()
            .setOrigins(ImmutableList.of(Cors.Origin.of(origin)))
            .setMethods(ImmutableList.of(method))
            .setResponseHeaders(ImmutableList.of(responseHeader))
            .setMaxAgeSeconds(maxAgeSeconds)
            .build();

    bucket.toBuilder().setCors(ImmutableList.of(cors)).build().update();

    System.out.println(
        "Bucket "
            + bucketName
            + " was updated with a CORS config to allow GET requests from "
            + origin
            + " sharing "
            + responseHeader
            + " responses across origins");
  }
}

C#

For more information, see the Cloud Storage C# API reference documentation.

You cannot currently configure CORS with the C# client library.

Go

For more information, see the Cloud Storage Go API reference documentation.

To set a CORS configuration for a bucket using Go, see the CORS reference documentation.

Node.js

For more information, see the Cloud Storage Node.js API reference documentation.

To set a CORS configuration for a bucket using NodeJS, see the Bucket reference documentation.

PHP

For more information, see the Cloud Storage PHP API reference documentation.

To set a CORS configuration for a bucket using PHP, see the Google\Cloud\Storage\Bucket reference documentation.

Python

For more information, see the Cloud Storage Python API reference documentation.

To set a CORS configuration for a bucket using Python, see the cors reference documentation.

Ruby

For more information, see the Cloud Storage Ruby API reference documentation.

To set a CORS configuration for a bucket using Ruby, see the Google::Cloud::Storage reference documentation.

REST APIs

JSON API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Create a JSON file that contains the following:

    {
    "cors": [
      {
        "origin": [
          "http://example.appspot.com"
        ],
        "method": [
          "GET"
        ],
        "responseHeader": [
          "Content-Type"
        ],
        "maxAgeSeconds": 3600
      }
    ]
    }
  3. Use cURL to call the JSON API with a PATCH Bucket request:

    curl --request PATCH \
    'https://storage.googleapis.com/storage/v1/b/[BUCKET_NAME]' \
    --header 'Authorization: Bearer [OAUTH2_TOKEN]' \
    --header 'Content-Type: application/json' \
    --data-binary @[JSON_FILE_NAME].json

    Where:

    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.
    • [OAUTH2_TOKEN] is the access token you generated in Step 1.
    • [JSON_FILE_NAME] is the path to the file you created in Step 2.

XML API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Create an XML file that contains the following:

    <?xml version="1.0" encoding="UTF-8"?>
    <CorsConfig>
    <Cors>
      <Origins>
        <Origin>http://example.appspot.com</Origin>
      </Origins>
      <Methods>
        <Method>GET</Method>
      </Methods>
      <ResponseHeaders>
        <ResponseHeader>Content-Type</ResponseHeader>
      </ResponseHeaders>
      <MaxAgeSec>3600</MaxAgeSec>
    </Cors>
    </CorsConfig>
    
  3. Use cURL to call the XML API with a Set Bucket CORS request:

    curl -X PUT --data-binary @[XML_FILE_NAME].xml \
    -H "Authorization: Bearer [OAUTH2_TOKEN]" \
    -H "x-goog-project-id: [PROJECT_ID]" \
    "https://storage.googleapis.com/[BUCKET_NAME]?cors"

    Where:

    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.
    • [OAUTH2_TOKEN] is the access token you generated in Step 1.
    • [PROJECT_ID] is the ID of the project associated with the bucket. For example, my-project.
    • [XML_FILE_NAME] is the path to the file you created in Step 2.

Viewing the CORS configuration for a bucket

To view the CORS configuration for a bucket:

gsutil

Use the gsutil cors command to get the CORS configuration of a bucket:

gsutil cors get gs://[BUCKET_NAME]

where [BUCKET_NAME] is the name of the bucket. For example, my-bucket.

Code samples

To view the CORS configuration for a bucket using the client libraries, follow the instructions for displaying a bucket's metadata and look for the CORS field in the response.

REST APIs

JSON API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Use cURL to call the JSON API with a GET Bucket request:

    curl -X GET \
    -H "Authorization: Bearer [OAUTH2_TOKEN]" \
    "https://storage.googleapis.com/storage/v1/b/[BUCKET_NAME]?fields=cors"

    Where:

    • [OAUTH2_TOKEN] is the name of the access token you generated in Step 1.
    • [BUCKET_NAME] is the name of the relevant bucket. For example, my-bucket.

XML API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Use cURL to call the XML API with a GET Bucket request:

    curl -X GET \
    -H "Authorization: Bearer [OAUTH2_TOKEN]" \
    "https://storage.googleapis.com/[BUCKET_NAME]?cors"

    Where:

    • [OAUTH2_TOKEN] is the name of the access token you generated in Step 1.
    • [BUCKET_NAME] is the name of the relevant bucket. For example, my-bucket.

Removing CORS from a bucket

To remove the CORS configuration from a bucket:

gsutil

  1. Create a JSON file that contains the following:

    []
  2. Use the gsutil cors command to configure CORS on a bucket:

    gsutil cors set [JSON_FILE_NAME].json gs://[BUCKET_NAME]

    Where

    • [JSON_FILE_NAME] is the path to the JSON file you created in Step 1.
    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.

REST APIs

JSON API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Create a JSON file that contains the following:

    {
    "cors": []
    }
  3. Use cURL to call the JSON API with a PATCH Bucket request:

    curl --request PATCH \
    'https://storage.googleapis.com/storage/v1/b/[BUCKET_NAME]' \
    --header 'Authorization: Bearer [OAUTH2_TOKEN]' \
    --header 'Content-Type: application/json' \
    --data-binary @[JSON_FILE_NAME].json

    Where:

    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.
    • [OAUTH2_TOKEN] is the access token you generated in Step 1.
    • [JSON_FILE_NAME] is the path to the file you created in Step 2.

XML API

  1. Get an authorization access token from the OAuth 2.0 Playground. Configure the playground to use your own OAuth credentials.
  2. Create an XML file that contains the following:

    <CorsConfig></CorsConfig>
  3. Use cURL to call the XML API with a Set Bucket CORS request:

    curl -X PUT --data-binary @[XML_FILE_NAME].xml \
    -H "Authorization: Bearer [OAUTH2_TOKEN]" \
    -H "x-goog-project-id: [PROJECT_ID]" \
    "https://storage.googleapis.com/[BUCKET_NAME]?cors"

    Where:

    • [BUCKET_NAME] is the name of the bucket. For example, my-bucket.
    • [OAUTH2_TOKEN] is the access token you generated in Step 1.
    • [PROJECT_ID] is the ID of the project associated with the bucket. For example, my-project.
    • [XML_FILE_NAME] is the path to the file you created in Step 2.

Troubleshooting CORS requests

If you run into unexpected behavior when accessing Cloud Storage buckets from a different origin, try the following steps:

  1. Use gsutil cors get on the target bucket to get its CORS configuration. If you have multiple CORS configuration entries, make sure that as you go through the following steps that the request values map to values in the same single CORS configuration entry.

  2. Check that you are not making a request to the storage.cloud.google.com endpoint, which doesn't allow CORS requests. For more information about supported endpoints for CORS, see Cloud Storage CORS support.

  3. Review a request and response using the tool of your choice. In a Chrome browser, you can use the standard developer tools to see this information:

    1. Click the Chrome menu Chrome menu icon. on the browser toolbar.
    2. Select More Tools > Developer Tools.
    3. Click the Network tab.
    4. From your application or command line, send the request.
    5. In the pane displaying the network activity, locate the request.
    6. In the Name column, click the name corresponding to the request.
    7. Click the Headers tab to see the response headers, or the Response tab to see the content of the response.

    If you're not seeing a request and response, it is possible that your browser has cached an earlier failed preflight request attempt. Clearing your browser's cache should also clear the preflight cache. If it doesn't, set the MaxAgeSec value in your CORS configuration to a lower value (the default value is 1800 (30 minutes) if not specified), wait for however long the old MaxAgeSec was, then try the request again. This performs a new preflight request, which fetches the new CORS configuration and purges the cache entries. Once you have debugged your problem, raise MaxAgeSec back to a higher value, to reduce the preflight traffic to your bucket.

  4. Ensure that the request has an Origin header and that the header value matches at least one of the Origins values in the bucket's CORS configuration. Note that the scheme, host, and port of the values must match exactly. Some examples of acceptable matches are as follows:

    • http://origin.example.com matches http://origin.example.com:80 (because 80 is the default HTTP port), but does not match https://origin.example.com, http://origin.example.com:8080, http://origin.example.com:5151, or http://sub.origin.example.com.

    • https://example.com:443 matches https://example.com but not http://example.com or http://example.com:443.

    • http://localhost:8080 only matches exactly http://localhost:8080, not http://localhost:5555 or http://localhost.example.com:8080.

  5. Ensure that the HTTP method of the request (if this is a simple request), or the method specified in Access-Control-Request-Method (if this a preflight request), matches at least one of the Methods values in the bucket's CORS configuration.

  6. If this is a preflight request, see if it includes one or more Access-Control-Request-Header headers. If so, then ensure that each Access-Control-Request-Header value matches a ResponseHeader value in the bucket's CORS configuration. All headers named in the Access-Control-Request-Header must be in the CORS configuration for the preflight request to succeed and include CORS headers in the response.

What's next