The same-origin policy is a security policy enforced on client-side web apps (e.g., web browsers) to prevent interactions between resources from different origins. While useful for preventing malicious behavior, this security measure also prevents useful and legitimate interactions between known origins. For example, a script on a page hosted from AppEngine at
example.appspot.com might want to use static resources stored in a Google Cloud Storage bucket at
example.storage.googleapis.com. However, because these are two different origins from the perspective of the browser, the browser won't allow a script from
example.appspot.com to fetch resources from
XMLHttpRequest because the resource being fetched is from a different origin.
The Cross Origin Resource Sharing (CORS) spec was developed by the World Wide Web Consortium (W3C) to get around this limitation. Google Cloud Storage supports this specification by allowing you to configure your buckets to return CORS-compliant responses. Continuing the above example, because Google Cloud Storage supports CORS, a browser can ask
example.storage.googleapis.com for permission to share its resources with scripts from
- How CORS Works
- Configuring CORS on a Bucket
- Sending a Cross-Domain Request to Google Cloud Storage
- Troubleshooting CORS-Related Problems
How CORS Works
On the client-side, when the web client (browser) makes a request to Google Cloud Storage, it automatically adds the
Origin header containing the origin of the resource seeking to share a cross domain's resources, for example,
Origin:http://www.example.appspot.com. Google Cloud Storage looks up the origin in the request header in its own CORS configuration to determine whether the incoming origin is allowed or not, and whether the incoming request method is allowed for that origin. If the origin and method are allowed, Google Cloud Storage includes the header
Access-Control-Allow-Origin in its response. The client (e.g., browser) checks this response header to verify that the domain in the response matches the domain specified in original request, and if these match, the request proceeds. If there is not a match, or if the Access-Control-Allow-Origin header is not present in the response, the request is disallowed.
Supporting CORS on the Client
Most clients (such as browsers) use the
XMLHttpRequest object to make a cross-domain request.
XMLHttpRequest takes care of all the work of inserting the right headers and handling the CORS interaction with the server. This means you don't add any new code to take advantage of CORS support, it will simply work as expected for Google Cloud Storage buckets configured for CORS.
Configuring CORS on a Bucket
Google Cloud Storage allows you to set CORS configuration at the bucket-level only. If you want to make a bucket available for cross-domain resource sharing, you set a CORS configuration on the bucket that contains all the origins you wish to share the bucket with, and the request methods that you want to allow on that bucket.
There are two ways to set CORS configuration on a bucket:
- Use the
gsutil cors setcommand to set or modify cors configuration. (Optionally, use
gsutil cors getto list a bucket's cors configuration.)
- Use the PUT Bucket method in the API, using the ?cors subresource to set or modify cors configuration. (Optionally, use GET Bucket with the ?cors subresource to list a bucket's cors configuration.
Whichever of the two methods you use to set CORS, gsutil or API, you have to supply the CORS configuration data in a cors XML body like the following, which specifies all the origins and request methods that are allowed to access the buckets:
<?xml version="1.0" encoding="UTF-8"?> <CorsConfig> <Cors> <Origins> <Origin>http://origin1.example.com</Origin> </Origins> <Methods> <Method>GET</Method> <Method>HEAD</Method> <Method>DELETE</Method> </Methods> <ResponseHeaders> <ResponseHeader>x-goog-meta-foo1</ResponseHeader> </ResponseHeaders> <MaxAgeSec>1800</MaxAgeSec> </Cors> </CorsConfig>
The fields for the CORS configuration XML are described in detail in the PUT Bucket method documentation.
Sending a Cross-Domain Request to Google Cloud Storage
You can use any allowed URI format to obtain a response from Google Cloud Storage that contains the CORS headers. For information about URI formats, see Request URIs
Troubleshooting CORS-Related Problems
If you run into unexpected behavior when accessing buckets using CORS, try the following steps:
gsutil cors geton the problem bucket to ensure the bucket has the expected CORS configuration.
- Capture a full request-response using a tool of your choice. In a Chrome browser you can use the standard developer tools to see this:
- In the Chrome upper right, click the wrench icon.
- Select Tools > Developer Tools
- Click the Network tab.
- Send your browser request and in the pane displaying the network activity, locate the request you're interested in.
- In the Name column, click the name corresponding to the request.
- Click the Headers tab to see the response headers, or the Response tab to see the content of the response.
- Ensure that the request actually has an Origin header
- Ensure that the Origin header you're sending matches at least one of the Origins in the CORS configuration you retrieved in Step 1. Note that the scheme, host, and port must all match. i.e.
http://origin.example.comdoes not match
https://origin.example.com, nor does it match
- Ensure that the Method you're sending (or the method specified in
Access-Control-Request-Method, if this a preflight request) is a match for one of the methods in your CORS configuration, that also is a match for Origin. To be applied, a CORS configuration entry must match on both Origin and Method. If you have two CORS configuration entries, one of which matches on Origin but not Method, and the other matches on Method but not Origin, neither one will be used, and no CORS headers will be included in the response.
- If this is a preflight request, check if the preflight request includes one or more
Access-Control-Request-Header. If so, then ensure that the matching CORS configuration entry includes a
<ResponseHeader>entry for each requested header. All headers named in the
Access-Control-Request-Headermust be in the CORS configuration for the preflight request to succeed and include CORS headers in the response.
- If you are trying to reproduce the problem, and you're not seeing a request/response, it is possible that your browser has cached an earlier failed preflight request attempt. Clearing your browser's cache may also clear the preflight cache.
The default setting for
<MaxAgeSec>is 1800 seconds (30 minutes). If you think clearing your browsers cache in the usual way is not also clearing the CORS preflight cache, you should set
<MaxAgeSec>on your CORS configuration to a lower value, wait 30 minutes, then try again. This should perform a new preflight request, which will fetch the new CORS configuration and also set
<MaxAgeSec>to the new lower value you selected, allowing the cache entries to be purged more frequently. Once you have debugged your problem, you should raise
<MaxAgeSec>back to a higher value, to reduce the preflight traffic to your bucket.