In this article, I will explain what CORS and CORS errors are and why you might run into them. I will present possible solutions and explain what preflight requests, CORS headers are, and why they are important in the communication between parties. The article assumes you have some basic knowledge of web development and the HTTP protocol. I tried to write the article in a way that is easy to understand for beginners and fill it with knowledge and try to avoid too many technical nuances that are not tightly connected with the CORS topic. If you spotted any mistakes or have any suggestions, do not hesitate to contact me. In some places, I made a simplification where a service means server and vice versa.
What is CORS?
Cross-Origin Resource Sharing (CORS) is an HTTP-based security mechanism controlled and enforced by the client (web browser). It allows a service (API) to indicate any origin other than its own from which the client can request resources. It has been designed in response to the same-origin policy (SOP) that restricts how a website (HTML document or JS script) loaded by one origin can interact with a resource from another origin. CORS is used to explicitly allow some cross-origin requests while rejecting others.
CORS is implemented primarily in web browsers, but it can also be used in API clients as an option. It's present in all popular web browsers like Google Chrome, Firefox, Opera, and Safari. The standard has been accepted as a W3C Recommendation in January 2014. Based on that, we can assume that it is implemented in all currently available and other than listed web browsers.
How does it work?
Everything starts on the client side, before sending the main request. The client sends a CORS preflight request to a service for resources with parameters in HTTP headers (CORS headers). The service responses using the same headers with different or the same values. The client decides, based on a CORS preflight response, if he can or cannot send the main request to the service. The web browser (client) will throw an error if the response does not meet the requirements of CORS preflight.
CORS preflight requests are sent regardless of the used libraries or frameworks to send requests from web browser. That's why you won't need to conform CORS requirements when working with API from your backend application.
CORS is not going to prevent users from requesting or downloading resources. You can still make a successful request
for a resource using apps like curl, Insomnia,
or Postman. CORS is only going to prevent the browser from accessing the resource if the
CORS policy does not allow it.
What is a CORS preflight?
When a browser sends a request to a server, it first sends an HTTP Options request. This is called a CORS preflight request. The server then responds with a list of allowed methods and headers. If the browser is allowed to make the actual request, it sends the actual request. If not, it shows an error and does not continue to send the main request.
CORS Headers
CORS headers are regular HTTP headers that are used to control the CORS policy. They are used in requests where the browser sends a CORS preflight request to the server, and the server responses with:
-
Access-Control-Allow-Origin
indicates what origin can fetch resources. Use one or more origins, e.g.:https://foo.io,http://bar.io
. -
Access-Control-Allow-Methods
indicates what HTTP methods are allowed. Use one or more comma HTTP methods, e.g.:GET,PUT,POST
. -
Access-Control-Allow-Headers
indicates what request headers are allowed. Use one or more headers, e.g.:Authorization,X-My-Token
. -
Access-Control-Allow-Credentials
indicates if sending cookies is allowed. Default:false
. -
Access-Control-Max-Age
- indicates how long the request result should be cached, in seconds. Default:0
.
If you decide to use Access-Control-Allow-Credentials=true
, then you need to be aware of the fact you cannot use wildcards *
in Access-Control-Allow-*
headers. It's required to explicitly list all allowed origins, methods, and headers.
See a full list of CORS headers.
Why is request blocked by CORS policy?
If you are a web developer, you have probably already seen or heard about CORS errors, and you have googled it many times to find the solution. The most common problem is that the browser blocks the request because of CORS policy. The browser will throw an error and show a log in the console:
Access to XMLHttpRequest at 'http://localhost:8080/' from origin
'http://localhost:3000' has been blocked by CORS policy:
Response to preflight request doesn't pass access control
check: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
The CORS error above notifies a user that the browser couldn't access a resource (https://localhost:8080
) from an origin (https://localhost:3000
) because the server didn't allow it. It happened because the server didn't respond with Access-Control-Allow-Origin
header with the origin or with a wildcard *
in the CORS preflight response.
A request may be blocked by CORS policy not only because of the incorrect origin, but also incorrect HTTP header, HTTP method or Cookie header.
How to fix CORS error?
The fundamental idea of "fixing CORS" is to respond to "OPTIONS" requests sent from a client with proper headers. There are many ways to start responding with the proper CORS. You can use a proxy server, or you can use middleware in your server.
Remember that Access-Control-*
headers are cached in a web browser according to the value set in Access-Control-Max-Age
header. Please make sure that you clear the cache before testing the changes. You can also disable caching in your browser.
1. Configure your server
By default, if you are a server owner, you need to configure CORS responses in your server and this is the only way to address the issue properly. You can achieve this in multiple ways and on multiple layers of your app. The most common way is to use reverse-proxy, API gateway, or any other routing service that offers to add headers to the responses. There are many services that you can use to do this, some of them are: HAProxy, Linkerd, Istio, Kong, nginx, Apache, Traefik. If your infrastructure contains only an application without any additional layers, then you can simply add CORS support in the application code.
Here are some popular examples of enabling CORS:
- Apache: modify .htaccess file,
- Nginx: modify configuration file,
- Traefik: use middlewares,
-
Spring Boot: use
@EnableCORS
annotation, -
ExpressJS: use
app.use(cors())
, - NextJS: use request helpers.
Here you can find more examples of enabling CORS in different frameworks and languages: enable-cors.org.
If you cannot enable CORS in the service, but you still want to make request to it, then you need to use one of the following solutions described below.
2. Install a browser extension
Using a browser extension might be a quick and easy way to solve your problems with CORS in your developer environment. The biggest advantage of using a browser extension is that you don't need to change your code or configuration. On the other hand, you need to install an extension on every web browser that you use for testing your web application.
Browser extensions alter incoming preflight request by adding the necessary headers to trick the browser. It's a handy solution to work locally with the production API that accepts requests only from the production domain.
You can find extensions in Google Web Store or in Mozilla Add-ons Library. In some cases, the default extension configuration might not be enough; make sure that the installed extension is configured properly. You should also be aware of that leaving the extension turned on indefinitely may cause problems on some websites. It's recommended to use them only for development purposes.
3. Disable browser CORS checks
You can disable CORS checks in your browser completely. To disable CORS checks in Google Chrome, you need to close the browser and start it with the --disable-web-security
and --user-data-dir
flags. By doing that, Google Chrome will not send CORS preflight requests and will not validate CORS headers.
# Windows
chrome.exe --user-data-dir="C://chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# macOS
open /Applications/Google\ Chrome.app --args --user-data-dir="/var/tmp/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
# Linux
google-chrome --user-data-dir="~/chrome-dev-disabled-security" --disable-web-security --disable-site-isolation-trials
All of those commands above start Google Chrome in an isolated sandbox. They will not affect your main Chrome profile.
See list of all available flags for Google Chrome.
4. Set up a proxy server
If you are looking for a solution that doesn't require you to change the browser settings, then you should look at the proxy server solution. This option helps you to overcome CORS errors without changing anything in the browser itself. The idea of using a proxy server is to send all requests to your server, and then the server will send the request to the actual service you want to use. You can build a proxy server on your own in a language and framework of your choice. You will need to configure CORS and implement a functionality to pass requests received requests to another service.
Proxy server is a good solution if you don't have access to the service you intend to use. There are ready to use and open-source proxy server services, but you should always ensure that they are not trying to intercept your requests with authorization headers and pass them to any 3rd party service. Such security breaches could be catastrophic failure for you and potential users of the service.
List of open-source CORS services that you can find on the internet:
- https://github.com/Freeboard/thingproxy
- https://github.com/bulletmark/corsproxy
- https://github.com/Rob--W/cors-anywhere
Please review the code of the newest version before using any of those services.
How to test CORS?
Using a browser to test your CORS configuration might be a tedious task. You can use a tool like CORS Tester, test-cors.org, or, if you are familiar with the command line, you can use curl to test your CORS configuration.
curl -v -X OPTIONS https://simplelocalize.io/api/v1/translations
Beware of false CORS errors
In some cases, when a service is behind an additional layer with rate-limiter, load balancer or authorization server, you can get a false CORS error. Blocked or rejected requests by a server should respond with error status codes. E.g.,:
- 401 unauthorized,
- 403 forbidden,
- 429 too many requests,
- 500 internal server error,
- any other than 2XX or 3XX.
You may see that the request is blocked due to failed preflight request, but in fact, a service just rejects it. You should always check the status code and response body of the response to avoid unnecessary debugging. The browser properly alerts you when a CORS preflight request fails, but the reason for the failure is not necessarily connected to the CORS configuration.
Conclusion
In this article, I tried to explain what CORS is, and what are the most common problems with it. I suggested 4 ways to fix CORS issues and explained the advantages and disadvantages of each one. I also explained how to configure CORS responses properly and how to test them. Moreover, I showed what are the most common issues with recognizing false CORS errors. I tried to put everything in simple terms and avoid technical nuances. If you have any questions, doubts, or suggestions, please do not hesitate to contact me.
Happy coding!
Top comments (24)
Really useful and clear.
Thank you for sharing this article !
That's a great explanation! Thank you!
Saving this one for later! Looks very good, self-explainatory graphics and possible solutions. 🔥
Thanks! 😎
Great artical & usefull ,saved
Oh, I thought it was that beer you can’t take east of the Mississippi. Nah, just kidding. Great article; thanks for the in-depth explanation.
CORS. the mother of all evil.
Great explanation! I am still new to the development world and CORS is one of those topics I have a hard time wrapping my head around. This article definitely helps!
Great explanation @jpomykala 👍
✌️😄
Great, thanks so much! CORS is one of those things I kept running into and in the beginning didn't really get it. Clear explanation, well done.
Thanks! I wanted to explain it as simple as I can. 😊
Thanks for this content, I was studying about it just today.
I hope that images with requests will help you to remember how this works! 😄