The dreaded CORS error is the bane of many a web developer’s day-to-day life. Even for experts, it can be eternally frustrating. Today, we’re digging into CORS, starting with the basics and working up to sharing specific code samples to help you enable CORS for your web development project. What is CORS? Why do you need it? And how can you enable it to save time and resources?
We’ll answer those questions so you can put the CORS bane behind you.
What Is CORS?
CORS stands for cross-origin resource sharing. To define CORS, we first need to explain its counterpoint—the same-origin policy, or SOP. The SOP is a policy that all modern web browsers use for security purposes, and it dictates that a web address with a given origin can only request data from the same origin. CORS is a mechanism that allows you to bypass the SOP so you can request data from websites with different origins.
Let’s break that down piece by piece, then we’ll get into why you’d want to bypass the same-origin policy in the first place.
What Is an Origin?
All websites have an origin, and it is defined by the protocol, domain, and port of its URL.
You’re probably familiar with the working parts of a URL, but they each have a different function. The protocol, also known as the scheme, identifies the method for exchanging data. Typical protocols are http, https, or mailto. The domain, also known as the hostname, is a specific webpage’s unique identifier. You may not be as familiar with the port as it’s not normally visible in a typical web address. Just like a port on the water, it’s the connection point where information comes in and out of a server. Different port numbers specify the types of information the port handles.
When you understand what an origin is, the “cross-origin” part of CORS makes a bit more sense. It simply means web addresses with different origins. In web addresses with the same origin, the protocol, domain, and port all match.
What Is the Same-origin Policy?
The same-origin policy was developed and implemented as a security measure against a specific website vulnerability that was discovered and exploited in the 2000s. Before the same-origin policy was in place, bad actors could use cookies stored in people’s browsers to make requests to other websites illicitly. This is known as cross-site request forgery, or CSRF, pronounced “sea surf.” It’s also known as “session riding.” Tubular.
Let’s say you log in to Netflix on your laptop to add Ridley Scott’s 1982 classic, “Blade Runner” to your queue, as one does. You click “Remember Me” so you don’t have to log in every time, and your browser keeps your credentials stored in a cookie so that the Netflix site knows you are logged in no matter where you navigate within their site.
Afterwards, you’re bored, so you fall down an internet rabbit hole wondering why “Blade Runner” is called “Blade Runner” when there are few blades and little running. You end up on a site about samurai swords that happens to be malicious—it has a script in its code that uses your authentication credentials stored in that cookie to make a request to Netflix that can change your address and add a bunch of DVDs to your queue (also, it’s 2006, and this actually happened). You’ve become a victim of cross-site request forgery.
To thwart this threat, browsers enabled the same-origin policy to prohibit requests from one origin to another.
Why Do You Need CORS?
While the same-origin policy helped stop bad actors from nefariously accessing websites, it posed a problem—sometimes you need or want assets and data from different origins. This is where the “resource sharing” part of cross-origin resource sharing comes in.
CORS allows you to set rules governing which origins are allowed to bypass the same-origin policy so you can share resources from those origins.
For example, you might host your website’s front end at www.catblaze.com, but you host your back-end API at api.catblaze.com. Or, you might need to display a bunch of cat videos stored in Backblaze B2 Cloud Storage on your website, www.catblaze.com (more on that below).
Do I Need CORS?
Let’s say you have a website, and you dabble in some coding. You’re probably thinking, “I can already use images and stuff from other websites. Do I need CORS?” And you’re right to ask. Most browsers allow simple http requests like get
, head
, and post
without requiring CORS rules to be set in advance. Embedding images from other sites, for example, typically requires a get
request to grab that data from a different origin. If that’s all you need, you’re good to go. You can use simple requests to embed images and other data from other websites without worrying about CORS.
But some assets, like fonts or iframes, might not work—then, you can use CORS—but if you’re a casual user, you can probably stop here.
Coding Explainer: What Is an http Request?
An http request is the way you, the client, use code to talk to a server. A complete http request includes a request line, request headers, and a message body if necessary. The request line specifies the method of the request. There are generally eight types:
get
: “I want something from the server.”head
: “I want something from the server, but only give me the headers, not the content.”post
: “I want to send something to the server.”put
: “I want to send something to the server that replaces something else.”delete
: …self-explanatory.patch
: “I want to change something on the server.”options
: “Is this request going to go through?” (This one is important for CORS!)trace:
“Show me what you just did.” Useful for debugging.
After the method, the request line also specifies the path of the URL the method applies to as well as the http version.
The request headers communicate some specifics around how you want to receive the information. There are usually a whole bunch of these. (Wikipedia has a good list.) They’re typically formatted thusly: name:value
. For example:
accept:text/plain
means you want the response to be in text format.
Finally, the message body contains anything you might want to send. For example, if you use the method post
, the message body would contain what you want to post.
Do I Need CORS With Cloud Storage?
People use cloud storage for all manner of data storage purposes, and most do not need to use CORS to access resources stored in a cloud instance. For example, you can make API calls to Backblaze B2 from any computer to use the resources you have stored in your storage buckets. If you’re running a mobile application and transferring data back and forth to Backblaze B2, for instance, you don’t need CORS. The mobile application doesn’t rely on a web browser.
You only need CORS if you’re specifically running code inside of a web browser and you need to make API calls from the browser to Backblaze B2. For example, if you’re using an in-browser video player and want to play videos stored in Backblaze B2.
Fortunately, if you do need CORS, Backblaze B2 allows you to configure CORS to your exact specifications while other cloud providers may have completely open CORS policies. Why is that important? An open CORS policy makes you vulnerable to CSRF attacks. To continue with the video example, let’s say you’re storing a bunch of videos that you want to make available on your website. If they’re stored with a cloud provider that has an open CORS policy, you have two choices—open or closed. You pick open so that your website visitors can call up those videos on demand, but that leaves you vulnerable to a CSRF that could allow a bad actor to download your videos. With Backblaze, you can specify the exact CORS rules you need.
If you are using Backblaze B2 to store data that will be displayed in a browser, or you’re just curious, read on to learn more about using CORS. CORS has saved developers lots of time and money by reducing maintenance effort and code complexity.
How Does CORS Work?
Unlike simple get
, head
, and post
requests, some types of requests can alter the origin’s data. These include requests like delete
, put
, and patch
. Any type of request that could alter the origin’s data will trigger CORS, as will simple requests that have non-standard http headers or requests in certain programming languages like AJAX. When CORS is triggered, the browser sends what’s called a preflight request to see if the CORS rules allow the request.
What Is a Preflight Request?
A preflight request, also known as an options
request, asks the server if it’s okay to make the CORS request. If the preflight request comes back successfully, then the browser will complete the actual request. Few other systems in computing do this by default, so it’s important to understand when using CORS.
A preflight request has the following headers:
origin
: Identifies the origin from which the CORS request originates.access-control-request-method
: Identifies the method of the CORS request.access-control-request-headers
: Lists the headers that will be included in the CORS request.
The web server then responds with the following headers:
access-control-allow-origin
: Confirms the origin is allowed.access-control-allow-method
: Confirms the methods are allowed.access-control-allow-headers
: Confirms the headers are allowed.
The values that follow these headers must match the values specified in the preflight request. If so, the browser will permit the actual CORS request to come through.
Setting CORS Up: An Example
To provide an example for setting CORS up, we’ll use Backblaze B2. By default, the Backblaze B2 servers will say “no” to preflight requests. Adding CORS rules to your bucket tells Backblaze B2 which preflight requests to approve. You can enable CORS in the Backblaze B2 UI if you only need to allow one, specific origin or if you want to be able to share the bucket with all origins.
If you need more specificity than that, you can select the option for custom rules and use the Backblaze B2 command line tool.
When a CORS preflight or cross-origin download is requested, Backblaze B2 evaluates the CORS rules on the file’s bucket. Rules may be set at the time you create the bucket with b2_create_bucket
or updated on an existing bucket using b2_update_bucket
.
CORS rules only affect Backblaze B2 operations in their “allowedOperations
” list. Every rule must specify at least one in their allowedOperations
.
CORS Rule Structure
Each CORS rule may have the following parameters:
Required:
corsRuleName
: A name that humans can recognize to identify the rule.allowedOrigins
: A list of the origins you want to allow.allowedOperations
: A list that specifies the operations you want to allow, including:- B2 Native API Operations:
B2_download_file_by_name
B2_download_file_by_id
B2_upload_file
B2_upload_part
- S3 Compatible Operations:
S3_delete
S3_get
S3_head
S3_post
S3_put
Optional:
allowedHeaders
: A list of headers that are allowed in a preflight request’s Access-Control-Request-Headers value.exposeHeaders
: A list of headers that may be exposed to an application inside the client.maxAgeSeconds
: The maximum number of seconds that a browser can cache the response to a preflight request.
The following sample configuration allows downloads, including range requests, from any https origin and will tell browsers that it’s okay to expose the ‘x-bz-content-sha1’ header to the web page.
[
{
"corsRuleName": "downloadFromAnyOrigin",
"allowedOrigins": [
"https"
],
"allowedHeaders": ["range"],
"allowedOperations": [
"b2_download_file_by_id",
"b2_download_file_by_name"
],
"exposeHeaders": ["x-bz-content-sha1"],
"maxAgeSeconds": 3600
}
]
You may add up to 100 CORS rules to each of your buckets. Backblaze B2 uses the first rule that matches the request. A CORS preflight request matches a rule if the origin header matches one of the rule’s allowedOrigins
, if the operation is in the rule’s allowedOperations
, and if every value in the Access-Control-Request-Headers
is in the rule’s allowedHeaders
.
Using CORS: Examples in Action
Using your browser’s console, you can copy and paste the following examples to see CORS requests succeed or fail. As a handy guide for you, the text files we’ll be requesting include the bucket configuration of the Backblaze B2 buckets we’re calling.
In the first example, we’ll make a request to get
the text file bucket_info.txt
from a bucket named “cors-allow-none” that does not allow CORS requests:
fetch(
'https://f000.backblazeb2.com/file/cors-allow-none/bucket_info.txt',
{
method: 'GET'
}
).then(resp => resp.text()).then(console.log)
As you can see, this request returns a CORS error:
Next, we’ll try the same request on a bucket named “cors-allow-all” that allows CORS with any origin, but only specific headers.
fetch(
'https://f000.backblazeb2.com/file/cors-allow-all/bucket_info.txt',
{
method: 'GET'
}
).then(resp => resp.text()).then(console.log)
When you run the code, you will see some text output to the console indicating that, indeed, the bucket allows CORS with all origins, but specific headers:
We didn’t include any headers in our request, so the request was successful and the text file we wanted—bucket_info.txt
—appears below the text output in the console. As you can see in the text output, the bucket is configured with an asterisk “*
,” also known as a “wildcard,” to allow all origins (more on that later).
Next, we’ll try the same thing on the bucket that allows CORS with all origins, but this time triggers a preflight check for a header that is not allowed:
fetch(
'https://f000.backblazeb2.com/file/cors-allow-all/bucket_info.txt',
{
method: 'GET',
headers: { 'X-Fake-Header': 'breaking-cors-for-fun' }
}
).then(resp => resp.text()).then(console.log)
Our bucket is configured to only allow the headers authorization
and range
, but we’ve included the header X-Fake-Header
with the value breaking-cors-for-fun
—definitely not allowed—in the request.
When we run this request, we can see another type of failure:
Below the request, but above the CORS errors, you’ll see that the browser sent an options
request. As we mentioned earlier, this is the preflight request that asks the server if it’s okay to make the get
request. In this case, the preflight request failed.
However, this request will succeed if we change our bucket settings to allow all headers.
fetch(
'https://f000.backblazeb2.com/file/cors-allow-all/bucket_info.txt',
{
method: 'GET',
headers: { 'X-Fake-Header': 'breaking-cors-for-fun' }
}
).then(resp => resp.text()).then(console.log)
Below, you can see the text output “This bucket allows CORS with all origins and any header values.”
The request was successful, and the text file we requested appears in the console.
At this point, it’s important to note that when configuring your own buckets, you should use caution when using the wildcard “*
” to allow any origin or header. It’s probably best to avoid the wildcard if possible. It’s okay to allow any origin to access your bucket, but, if so, you’ll probably want to enumerate the headers that matter to avoid CSRF attacks.
For more information on using CORS with Backblaze B2, including some tips on using CORS with the Backblaze S3 Compatible API, check out our documentation here.
Stay on CORS
Ah, another inevitable CORS pun. Did you see it coming? I hope so. In conclusion, here are a few things to remember about CORS and how you can use it to avoid CORS errors in the future:
- The same-origin policy was developed to make websites less vulnerable to threats, and it prevents requests between websites with different origins.
- CORS bypasses the same-origin policy so that you can share and use data from different origins.
- You only need to configure CORS rules for your Backblaze B2 data if you are making calls to Backblaze B2 from code within a web browser.
- By setting CORS rules, you can specify which origins are allowed to request data from your Backblaze B2 buckets.
Are you using CORS? Do you have any other questions? Let us know in the comments.