What is CORS?
CORS, which stands for “Cross-Origin Resource Sharing,” is a security standard that enables servers to indicate the origins from which browsers are allowed to request resources. It was created to refine the same-origin policy (SOP), which browsers use to prevent malicious applications from accessing sensitive data on domains they do not control.
In this article, we’ll start by reviewing the importance of the SOP before discussing how CORS works. We’ll then explore the benefits and best practices for implementing CORS effectively.
What is the same-origin policy?
The same-origin policy (SOP) is a browser security feature that restricts how resources can be accessed by different web applications. This policy requires that a resource must be from the same origin as the web application that is attempting to access it.
For two origins to be considered the same, they must have the same host, scheme, and port. Let’s take this URL for example:
https://blog.postman.com:3000. The scheme is HTTPS, the host is
blog being a subdomain of
postman.com), and the port is
3000. The port number is typically excluded for origins with a domain name, but it is included in our example for reference purposes.
An origin with an “http” scheme is not the same as an otherwise identical origin with an “https” scheme. However,
https://blog.postman.com has the same origin as
https://blog.postman.com/tag/api-first because they have the same scheme, host, and port.
While the SOP restricts cross-origin access to resources, it is more permissive for certain types of resources, including:
- Images: Webpages can embed images from any origin without any restrictions.
- CSS stylesheets: External CSS can be loaded on a webpage by using the link tag directly in the HTML or the @import rule in a different stylesheet file.
- Media files: Audio and video files can be embedded and played from different origins using the
How does CORS work?
While the same-origin policy, which is strictly implemented on the client side, sets a default restriction on access to some resources, CORS enables the servers that are hosting these resources to specify who they want to give access to. It therefore overrides the restriction that the SOP places on these resources. CORS relies on HTTP request and response headers to communicate with and get permission from the resource server.
There are two primary types of CORS requests: simple requests and preflight requests.
Simple requests are CORS requests that are sent as a part of the main HTTP request. For a simple request to be sent to the server, the following criteria must be met:
- The request must use the GET, HEAD, or POST HTTP method.
- The request headers must be simple headers, which are HTTP headers that are safe to be automatically set by user agents. Simple headers include
- If the request uses the POST method, which indicates that the request has a request body, the
Content-Typeheader must have a value of
Preflight requests are much more complex than simple requests. They are used when:
- The original request uses a method other than GET, HEAD, or POST. Typically, these requests use the DELETE and PUT methods.
- The original request uses content types other than
text/plainin POST requests.
The browser sends a “preflight request” to the server for any action that mutates data. This is because requests that update data are not considered safe, so the browser first makes sure the server allows CORS before sending the actual request itself.
The preflight request uses the OPTIONS HTTP method to confirm that the server will allow the original request. The browser also includes the following headers with the preflight requests:
Access-Control-Request-Method: This header contains the HTTP method that will be used when the browser makes the actual request. It tells the server what to expect when the real request is made.
Access-Control-Request-Headers: This is a list of headers that will be sent with the actual request, including custom request headers.
If the preflight request is successful and the browser confirms that the server allows CORS, the actual request will be sent to the server. Otherwise, the browser won’t make the request and will instead receive a CORS error.
The server uses response headers to share information about CORS with the browser. The response to a CORS request will typically contain the following headers:
Access-Control-Allow-Origin: This header tells the browser which origins can access its resources. It can be set to the value of the origin itself (for example,
https://postman.com), or it can use a wildcard (*) instead.
Access-Control-Allow-Credentials: This header is a boolean value that indicates whether the browser should include credentials (like cookies or HTTP authentication credentials) when making a cross-origin request. If this header is set to false in the response to a preflight request, the credentials are not included in the actual request. If a simple request is made with this header set to false, the browser will not include this credential in the actual request.
Access-Control-Allow-Methods: This header is used in a response to a preflight request to indicate which request methods are allowed in the main request.
Access-Control-Allow-Headers: This header is used in a response to a preflight request to indicate which headers are allowed in the main request.
Access-Control-Max-AgeThis header specifies the maximum time (in seconds) for which the response to a preflight request can be cached.
A successful response to a CORS request can include any HTTP status code as long as it contains one or more of the response headers listed above. However, a successful response to a CORS preflight request must indicate an OK status (i.e., 200 or 204).
What are the benefits of working with CORS?
CORS improves an application’s overall security posture while improving flexibility and interoperability. Its main benefits include:
- Enhanced API security: CORS helps prevent malicious applications from making unauthorized cross-origin requests to sensitive resources on a different domain. By relaxing the same-origin policy (SOP) and requiring explicit permission through CORS headers, it reduces the risk of cross-site request forgery (CSRF) attacks and unauthorized data access.
- Cross-origin authentication: CORS enables secure cross-origin authentication by permitting the transmission of credentials (such as cookies or authentication tokens) in cross-origin requests. This is essential for Single Sign-On (SSO) and other authentication mechanisms.
- Standardization: CORS is a standardized mechanism that is supported by all major web browsers. This uniform implementation across browsers ensures a consistent and reliable approach to handling cross-origin requests.
- API integration: CORS is essential for integrating web applications with external APIs, enabling developers to utilize third-party functionality in their own services.
What are some best practices for implementing CORS?
While CORS was designed to securely override the same-origin policy, it comes with its own security considerations. Improper implementation of CORS can put your application at risk of data breaches and sensitive data exposure. It is therefore important to follow the recommended industry best practices, such as:
- Avoid cache poisoning: If the server’s
Access-Control-Max-Ageheader is set to a long duration and the response is cached, outdated permissions may be used for subsequent requests, leading to potential security issues. Ensure that
Access-Control-Max-Ageis set to a moderate duration (the default and recommended duration is five seconds).
- Be explicit: It is not recommended to use wildcards (*). Instead, explicitly specify the origins that are allowed to access the server’s resources by using the
Access-Control-Allow-Originheader. You should also explicitly specify the allowed methods and headers using
Access-Control-Allow-Headers. Only use wildcards when you’re very sure you want the API to be accessible from any origin.
- Include the correct CORS headers: Ensure that the server includes all the necessary headers in its CORS response. Not doing so can expose the application to security risks. For example, missing the
Access-Control-Allow-Credentialsheader can be problematic if the browser is not returning credentials when it should. If it is set to an unintended value, the browser could be sharing credentials with an unwanted origin.
- Document everything: It is important to adequately document your CORS policies and make them known to developers who might integrate your APIs in their application. This practice helps provide a better developer experience and builds trust with your API’s consumers.
- Validate credentials: CORS ignores credentials by default. If you need to include credentials (such as cookies or HTTP authentication credentials) in cross-origin requests, set
true. However, you must also ensure that the server validates and handles credentials securely.