Cross-Origin Resource Sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the resource originated. CORS defines a way in which a browser and server can interact securely to determine whether it is safe to allow a cross-origin request.
What is CORS?
CORS allows servers to specify who can access the assets on the server, among many other things. CORS works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser.
Additionally, for HTTP request methods that can cause side-effects on server data (in particular, HTTP methods other than GET, or POST with certain MIME types), the specification mandates that browsers preflight the request, soliciting supported methods from the server with the HTTP OPTIONS request method, and then, upon getting a response that does not disallow the requested method, sending the actual request.
Servers can also notify clients if they specify who is allowed to access a resource, by using a new “Access-Control-Allow-Origin” header. CORS defines a way in which a browser and server can interact to determine whether it is safe to allow the cross-origin request. It allows sharing of resources cross-origin.
Why is CORS needed?
CORS is needed because modern web applications extensively use APIs to access data and functionality from various domains. Previously, the same-origin security policy in web browsers prevented scripts within a web page from making requests to a different domain than the one which served the web page.
This policy was intended to prevent malicious sites from reading sensitive data from a victim site. However, this also prevented legitimate use cases where a web application may need to access data from multiple sources. CORS provides a secure mechanism that allows a server to temporarily ease the same-origin policy and allow some cross-origin requests while rejecting others.
How does CORS work?
CORS works by adding new HTTP headers that allow servers to describe the set of origins that are permitted to read that information using a web browser. Additionally, for HTTP request methods that can cause side-effects on server data, the browser preflights the request, soliciting supported methods from the server with HTTP OPTIONS, and then upon approval, sends the actual request with the actual HTTP request method.
The CORS workflow includes the following steps:
- A client (browser) sends an HTTP OPTIONS request header to the server to check if the server allows requests from the requesting origin.
- The server responds with a list of allowed HTTP methods and headers in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers.
- The browser automatically sends the actual request with the actual HTTP request method if the method is allowed.
- For GET and HEAD requests, the browser directly sends the request with an Origin header after preflight is successful.
- For requests with side effects like POST, PUT, DELETE, the browser first sends a preflight OPTIONS request to confirm the actual request method is allowed.
- The server returns a response that confirms the request method is allowed. This also includes an Access-Control-Allow-Origin header that specifies which origins can access the resource.
- The browser sends the actual request if the preflight was successful.
The browser automatically handles the preflight OPTIONS request. The CORS workflow ensures cross-origin requests are secure and allowed by the server before sending the actual request. The server needs to respond with the appropriate CORS headers like Access-Control-Allow-Origin for cross-origin requests.
Common CORS Terminology
Here are some common terms used with CORS:
- Origin – Refers to the domain sending the HTTP request. Eg: https://www.example.com
- Cross-Origin – A cross-origin request is one that is made to a domain other than the one which served the base web page.
- Preflight Request – The preparatory OPTIONS request sent by the browser for CORS. This checks if the actual request is safe to send.
- Actual Request – The actual GET/POST/PUT/DELETE request following the preflight to get or modify data.
- Simple Requests – Requests that do not trigger a CORS preflight. Only GET, HEAD and POST requests with certain content types are considered simple requests.
- Credentials – Authorization cookies, TLS client certificates, and CORS disables sending credentials by default for cross-origin calls.
Ways to enable CORS
There are several ways to enable CORS in an API or web application. The most common options are:
1. CORS Middleware
Most server frameworks like Express, Django, Ruby on Rails have CORS middleware that can be configured to enable CORS with various options. For example, in Express:
// Import cors const cors = require('cors') // Enable CORS for all routes app.use(cors())
2. Custom CORS Headers
You can send custom CORS headers like Access-Control-Allow-Origin from your code to enable CORS:
“`js
// Allow CORS from any origin
res.setHeader(‘Access-Control-Allow-Origin’, ‘*’);
// Allow CORS from specific origins
res.setHeader(‘Access-Control-Allow-Origin’, ‘https://www.example.com’);
“`
3. Proxy
A proxy server can handle CORS requests and add the required headers to remove the need to add CORS at the application level.
4. Nginx
Nginx and other proxies/load balancers can also add CORS headers. For Nginx:
“`
add_header ‘Access-Control-Allow-Origin’ ‘*’;
“`
Enabling CORS in Common Frameworks/Languages
Here are examples of how to enable CORS in some popular server-side frameworks and languages:
Node.js & Express
“`js
// Import CORS
const cors = require(‘cors’);
// Enable All CORS Requests
app.use(cors());
// Enable CORS for a Single Route
app.get(‘/products’, cors(), (req, res) => {
res.json({msg: ‘This route has CORS enabled’});
})
“`
Ruby on Rails
In `config/application.rb`:
“`ruby
# Allow CORS from any origin
config.middleware.insert_before 0, Rack::Cors do
allow do
origins ‘*’
resource ‘*’,
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
“`
Django
Install `django-cors-headers` and add to middlewares:
“`python
# settings.py
INSTALLED_APPS = [
# …
‘corsheaders’,
# …
]
MIDDLEWARE = [
# …
‘corsheaders.middleware.CorsMiddleware’,
‘django.middleware.common.CommonMiddleware’,
# …
]
CORS_ORIGIN_ALLOW_ALL = True # Allow all origins
“`
PHP
Using custom headers:
“`php
// Allow CORS from any origin
header(‘Access-Control-Allow-Origin: *’);
// Allow CORS from specific origin
header(‘Access-Control-Allow-Origin: https://www.example.com’);
“`
Important CORS Headers
Here are some of the important HTTP response headers used by CORS:
Header | Description |
---|---|
Access-Control-Allow-Origin | Specifies allowed origins separated by comma if more than one |
Access-Control-Allow-Methods | Specifies allowed HTTP methods separated by comma |
Access-Control-Allow-Headers | Specifies allowed request headers separated by comma |
Access-Control-Allow-Credentials | Indicates if credentials like cookies are allowed |
Access-Control-Max-Age | Specifies how long preflight results can be cached |
CORS Security Considerations
When enabling CORS, it’s important to balance functionality and security. Here are some key factors to consider:
- Don’t use
Access-Control-Allow-Origin: *
blindly as it allows any website to make cross-origin requests. Be as specific as possible in declaring allowed origins. - Use CORS middleware in frameworks wherever possible instead of setting custom headers manually.
- Only allow required HTTP methods instead of simply allowing all methods.
- Set
Access-Control-Allow-Credentials: true
only if required to allow sending cookies/credentials. - Add additional middleware to protect against Cross-Site Request Forgery (CSRF).
- Enable CORS on a route level basis instead of globally if possible.
Conclusion
CORS provides a powerful way to allow cross-origin requests to APIs and resources on a web server. Modern web applications rely extensively on APIs and CORS serves as the gateway for secure cross-origin data fetches.
The browser handles most of the complexity behind the scenes. Servers simply need to respond with appropriate CORS headers to enable CORS and specify restrictions. With proper security precautions, CORS can enable rich web apps that can take advantage of resources across multiple domains.