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 it works?
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.
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.
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.
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.,:
500 internal server error
429 too many requests
403 forbidden
401 unauthorized
- 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.
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. 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
3. 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.
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?
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 http://codino.pl
Conclusion
Conclusion Cross-Origin Resource Sharing is a critical aspect of web development that ensures security, scalability, and an improved user experience. By correctly implementing CORS, developers can take full advantage of the interconnected web while maintaining the necessary security measures to protect user data and privacy. Understanding CORS and its implementation is a valuable skill for any web developer, as it opens up a world of possibilities for creating dynamic and feature-rich web application
I recommend reading the detailed MDN article if you want to learn more about CORS details.