October 12, 2022
Troubleshoot CORS related issues on API requests
Cross Origin Resource Sharing (a.k.a. CORS) is a powerful, and yet misunderstood, web standard for protecting web APIs from abuse. If you’re anything like me, however, you had your fair share of wasted work hours trying to deal with it from time to time. The concept seems deceptively simple, but the devil is in the details.
First of all, its important to understand that CORS compliance is an optional standard and is mostly enforced by browsers, like Chrome, Firefox and Safari. Therefore, it’s unlikely that you will face issues with it while working with libraries like Python’s Requests or CLI tools like curl.
However, if you have a frontend application running on your browser with a domain like, let’s say, http://localhost:3000
, API calls directed to different domains, like https://laury.dev
, will be preceded by a preflight request. This determines if the browser will execute the main request or not.
When having problems with this process, you may see an error similar to this on your Chrome console:
Access to XMLHttpRequest at ‘https://laury.dev from origin ‘http://localhost:3000 has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
To reproduce the problem in a more isolated manner, you can use curl
to make a preflight request manually. The important parts here are:
- the HTTP method has to be
OPTIONS
- three headers must be present:
Access-Control-Request-Method
,Access-Control-Request-Headers
andOrigin
. Be wary, however, that the domain stated onOrigin
must mention protocol, FQDN, port (if any is being used), and no trailing slash.
Here’s an example:
curl -IX OPTIONS https://google.com \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: origin, x-requested-with" \
-H "Origin: http://mywebsite.com:3000"
For a preflight request to be considered successful, all of the following must be true:
- the HTTP status on the response is within 200-299
- the response contains these headers:
Access-Control-Allow-Methods
,Access-Control-Max-Age
andAccess-Control-Allow-Origin
. One extra catch here is that this last header cannot present the value*
when the main request contains authentication information (like cookies). If it does, the preflight will be considered a failure by the browser. In this case, the header has to mention the origin domain explicitly.
Kubernetes users running Ingress Nginx
To manage CORS headers on Ingress Nginx, you may use these annotations on Ingress objects. Even objects that only manage paths inside domains will work with them, so there’s no need to enable CORS on the entire domain if you don’t feel like it.
Further reading
Light overview of preflight requests
Official specification of the Fetch standard for preflight requests