Any Front-end dev building modern web apps would feel very familiar with this error in the browser console, and most likely have fixed it several times with some magic snippets found from Google. But this time let's dig in a bit more to better understand why these fixes work.
What CORS is
It all starts with the the default browser security mechanism same-origin policy, which does not allow AJAX requests sent to a different origin other than the one serving your web page and JS files.
A origin is a combination of protocol
, host
and port
. So CORS (Cross-Origin Resource Sharing) is a security relaxation to get around this browser security feature as sometimes we do need to allow requests to go to another origin for legitimate reasons as commonly seen in the SPA setup. (e.g api server is hosted separately or need to communicate to external api)
Why CORS is needed
As described in What, it is to get around the same-origin
policy. But why we have this policy in the first place? Well browser is like a crowded marketplace where any link can be opened here, same origin is like providing a safe sandbox to isolate pages from different sites. Consider the contrived scenario below:
Thanks to the same-origin
policy, the fake site cannot take advantage of the credentials stored in browser cookie and make requests to the actual server to possibly gain access to protected resources or perform harmful actions. So you money is safe 💰
Now imagine the server is hosted in another origin (different host), we want the real web page to still be able to communicate with the server but block the fake page's requests as same-origin
. This is what CORS comes to mitigate.
How CORS works
In general, it works by setting a few HTTP Headers between browser and web server. Depending on if the request is a simple request, the flow will be different as simple requests normally do not cause side effect on server resources, e.g change status or delete resources.
▪︎ Simple Request Flow:
In this case, browser directly sends the simple request with header origin: https://app.domain.com
. If server returns response with header access-control-allow-origin
containing the origin
in the request, the browser will continue to consume the response.
▪︎ Preflight Request Flow:
When the request does not meet the simple request criterias, (e.g request method is DELETE
or contain custom headers ), as shown in above animation, this time browser will first send a preflight request with method: OPTIONS
. It basically says:
"I want to make a POST request to api.domain.com" from app.domain.com with Content-Type, Accept and custom Authorization headers, is this allowed?"
If the server responds with a set of Access-Control-*
headers covering those requested in OPTIONS request, the actual POST request will be sent.
In browser network tab, you probably have noticed sometimes there are prefligt request sent before the real one.
Check this article for Reduce preflight overhead
The last scenario is when request contains credentials, e.g cookie header. It has the same flow as either simple or preflight, but require extra response header Access-Control-Allow-Credentials: true
, and must not use *
wildcard in other Access-Control-*
headers.
That's it, thanks for reading the first post! Hopefully with this quick guide it makes more sense when you search around fixes for the CORS errors.
Reference
MDN CORS Doc
Complete Guide to CORS
3 Ways to Fix the CORS
Visualised Explanation for CORS
Drawing with Excalidraw, and this nice tool built by @dai_shi to make drawing animate