By now, every web developer has heard of Cross-Origin Resource Sharing-or CORS for short-but few actually understand it. In this post, we attempt to explain CORS from the very ground zero up to the most common pitfalls and the best practices using the technology. Be it an experienced developer or a candidate aiming to ace your software engineering interview; this article will equip you with the knowledge needed to handle CORS.
What is CORS? The Same-Origin Policy Before explaining CORS, there is a need to have an understanding of the Same-Origin Policy. The Same-Origin Policy is a security policy implemented in all web browsers. It confines how resources from one origin interact with resources from another. An origin is defined as the combination of: Protocol, for example - https:// Domain, for example - example.com Port, for example - :3000
For example :
https://example.com
andhttps://api.example.com
are different origins.https://example.com:3000
andhttps://example.com
are also different origins.
SOP prevents malicious scripts from one origin from accessing sensitive data from another. However, modern web applications often need to make cross-origin requests. That is where CORS comes in.
CORS is a protocol to let servers specify who can access their resources and how. It is a set of rules to safely communicate with different origins through browsers and servers.
Whatever the kind of violation of any CORS policy, the browser will block that request and throw some error in the developer console.
- CORS Policy Error
Fetching resource at 'https://api.example.com/data' from origin 'https://frontend.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
- Preflight Failure
OPTIONS https://api.example.com/data net::ERR_FAILED
- Mismatched Methods or Headers
fetch at 'https://api.example.com/data' from origin 'https://frontend.com' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
When a browser makes a cross-origin request, it checks the CORS headers in the server's response to decide whether to allow or block the request.
-
Simple Requests
- For a GET or POST request with simple headers, the browser sends the request straightaway.
- For cross-origin requests, browsers allow access if the server includes an
Access-Control-Allow-Origin
header in the response.
-
Preflight Requests
- For non-simple requests - for example, PUT or DELETE, or requests with custom headers - the browser first sends a preflight OPTIONS request.
- The server must respond with appropriate CORS headers to allow the actual request.
Image credit: MDN
Following is how one might configure CORS on popular backend frameworks:
const express = require('express');
const cors = require('cors');
const app = express();
// Basic CORS configuration
app.use(cors({
origin: 'https://frontend.com', // Allow only this origin
methods: ['GET', 'POST'], // Allowed methods
allowedHeaders: ['Content-Type'] // Allowed headers
}));
app.get('/data', (req, res) => {
res.json({ message: 'Hello, CORS!' });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Installation Install django-cors-headers
using pip:
pip install django-cors-headers
Add the following configurations:
INSTALLED_APPS = [
.
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
.
]
CORS_ALLOWED_ORIGINS = [
'https://frontend.com',
]
# Allow specific methods or headers - Optional
CORS_ALLOW_METHODS = ['GET', 'POST']
CORS_ALLOW_HEADERS = ['content-type']
A preflight request is an OPTIONS request sent by the browser to check if the server supports the actual request.
app.options('/data', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'https://frontend.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.status(204).send(); // No Content
});
- Use permissive configurations, for ease of testing.
- Example: Allow all origins and methods.
app.use(cors());
- Origins to be restricted to trusted domains.
- Avoid using
*
inAccess-Control-Allow-Origin
. - Install HTTPS to ensure secure communication.
- CORS is a client-side feature:
- CORS is implemented on the server-side, not on the client-side.
-
Setting
Access-Control-Allow-Origin: *
is safe:- This allows any origin; this may expose your API to abuse.
-
CORS protects the server:
- CORS is a browser-enforced policy and does nothing to protect the server from malicious requests.
-
Whitelist Trusted Origins
- Avoid using
*
in production.
- Avoid using
-
Limit HTTP Methods
- Only allow necessary methods, for example, GET, POST.
-
Validate Preflight Requests
- Respond only to valid OPTIONS requests.
-
Enable Authentication Safely
- Allow credentials only when necessary:
app.use(cors({
origin: 'https://frontend.com',
credentials: true
}));
- Use HTTPS
- Always serve your API over HTTPS for security.
-
Check Developer Tools
- Check CORS headers within the Network tab of browser dev tools.
-
Check Server Configuration
- The server should include the necessary CORS headers in its response.
-
Mock Requests
- Use programs like Postman to ignore CORS and verify server behavior.
-
Preflight Responses
- Make sure OPTIONS requests are handled appropriately by the server.
CORS is a common subject when interviewing web developers. Be prepared to:
- Explain the Same-Origin Policy.
- Differentiate between simple and preflight requests.
- Write basic CORS configurations for a given backend.
- Debug sample CORS errors.
Although CORS can appear intimidating, it is manageable once one masters the mechanics. Follow best practices and tailor the configurations toward the needs of your application, and you will have secure, seamless cross-origin interactions.