Understanding CORS

Marek Krzeszowiec

13 Nov

10 minutes

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 preflight: Server-Client Requests Scheme

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.

cors request and response 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:

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.

Use browser extension to overcome CORS error

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.

Disable security in Google Chrome to overcome CORS error

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.

Use proxy server to overcome CORS error

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:

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.