CORS errors, same-origin policy and pre-flighted requests

Vatsal Patel

Vatsal Patel / March 21, 2022

4 min read––– views


If you've ever built a website you'll have certainly faced a CORS error (Cross-Origin resource sharing) somewhere along the line. The browser complains in striking red color with "no access-control-allow-origin header is present on the requested resource" or "strict origin when cross origin" or "missing allow origin header" messages in the console. People, including myself, usually just implement the first answer they find on StackOverflow and forget about it ... until it gets flagged by a security team. I finally delved a bit deeper and found answers to the questions I always had about CORS and here they are.

What problem does CORS solve?

Browsers by default block read access to response data returned from API requests (but still allow the request to be made) to another origin* due to a thing called the same-origin policy. For example, if you go to your browser won't allow you to make a request to from the browser's console. But this is a bit too restrictive for the modern age website and so CORS was added to relax this and allow certain cross-origin requests.

*An origin is identified by three things: the protocol, a hostname and a port. For example uses the https scheme, as its host and port 443 (the default for https).

Ah, so CORS actually relaxes security instead of tightening it?

Yes! Without CORS the same-origin policy would be enforced in its strictest form. By adding CORS you are relaxing the constraints and allowing chosen origins to read cross domain request responses.

Okay then, why was same-origin policy introduced in the first place?

The same-origin policy makes the browser safer and reduces possible attack vectors by restricting how a document or script loaded by one origin can interact with a resource from another origin. So it's a safety first kind of set up. Keep everything locked and only relax the rules through CORS if needed.

Why doesn't the same-origin policy prevent the request to be made in the first place instead of just preventing the browser to read the response?

The same-origin policy actually allows certain cross-origin writes such as links, redirects and form submission and cross-origin embedding such as images. Disabling all request would be way too restrictive and break a large part of the web.

How do I set up CORS?

The API needs to be configured to send back one origin in the Access-Control-Allow-Origin header that is allowed to read the response data. You can also specify the * wildcard which allows any origin to read the response but this should largely be avoided since it completely disables the same-origin policy and removes any security benefits.

How do I set multiple origins to be allowed through CORS?

This allow-origin header only allows one value to be set. So something like

"Access-Control-Allow-Origin": ""

isn't allowed . If you'd like your API to work for multiple origins you have to write some code in your API which obtains the origin from the request, checks it against a whitelist you've defined and then sets the request origin as the value for this response header. This would make the header's value dynamic and change it depending on the request origin, instead of one static origin value.

Why do I see two requests for some calls?

Some requests are treated a bit more securely and are called pre-flighted requests since they have the potential to cause damage. They require the browser to first send a HTTP OPTIONS request to find out if the actual request is safe to send. The server responds with a series of headers which tells the browser which requests are safe and which aren’t. Only then does the browser make the original request to the API.

So CORS and same-origin policy help to secure my API?

These don't secure your API. Any bad actor can call your API via a simple curl or through apps like postman. Same origin policy only affects requests made through the browser. It's protecting the user from malicious websites trying to take advantage of the browser's "environment". Your API needs its own security in place to stop malicious actors from causing damage.

Special shout out to Danyal Khan and the rest of my colleagues at SigTech for discussing this with me and questioning my understanding!