Handling CORS (Cross-Origin Resource Sharing)
Cross-Origin Resource Sharing (CORS) is a security mechanism implemented by web browsers to prevent unauthorized access to resources from a different origin. This document provides guidance on understanding and properly handling CORS in web applications.
What is CORS?
CORS is a protocol that allows a web application running on one origin (e.g., https://example.com
) to access resources on another origin (e.g., https://api.example.com
). Browsers enforce CORS to prevent potential cross-origin attacks, such as CSRF (Cross-Site Request Forgery).
Key Terms
- Origin: A combination of scheme (protocol), host (domain), and port (e.g.,
https://example.com:443
). - Preflight Request: A request sent by the browser to check if the server permits cross-origin requests.
Common Scenarios Requiring CORS
Quote
- Accessing a third-party API.
- Communicating between subdomains (e.g.,
app.example.com
andapi.example.com
). - Hosting your frontend and backend on different origins.
How CORS Works
Simple Requests
For simple requests (e.g., GET
or POST
with specific headers), the browser sends the request directly to the server.
Preflight Requests
For complex requests (e.g., PUT
, DELETE
, or requests with custom headers), the browser sends a preflight OPTIONS
request to the server to determine if the actual request is allowed.
Server Response
The server must respond with appropriate CORS headers, such as:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
Setting Up CORS
Configuring CORS on the Server
CORS headers need to be configured on the server-side to allow cross-origin requests. Below are examples for popular backend frameworks.
Node.js with Express
const cors = require("cors");
const express = require("express");
const app = express();
const corsOptions = {
origin: "https://example.com",
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
};
app.use(cors(corsOptions));
app.get("/api", (req, res) => {
res.json({ message: "CORS is configured!" });
});
app.listen(3000, () => console.log("Server running on port 3000"));
Python with Flask
Using Proxy Servers
If you cannot modify the server, set up a proxy to handle CORS. For example, configure a proxy in a React app:
import { defineConfig } from "vite";
export default defineConfig({
server: {
proxy: {
"/api": {
target: "https://api.example.com",
changeOrigin: true,
},
},
},
});
Debugging CORS Issues
Common Errors
- No 'Access-Control-Allow-Origin' header: The server did not respond with a valid CORS header.
- CORS preflight request failed: The server did not respond to the preflight
OPTIONS
request.
Tools for Debugging
- Use browser developer tools to inspect network requests and responses.
- Check the server logs for CORS-related errors.
Checklist
- Verify the server includes the correct
Access-Control-Allow-Origin
header. - Ensure preflight requests are handled correctly on the server.
- Confirm that the browser and server are communicating over HTTPS.
Security Considerations
Restrict Allowed Origins
-
Avoid using
*
as the value forAccess-Control-Allow-Origin
in production. -
Specify exact origins:
Validate Tokens
- Use proper authentication mechanisms (e.g., JWTs) to validate API requests.
Limit Methods and Headers
- Restrict
Access-Control-Allow-Methods
andAccess-Control-Allow-Headers
to only what is necessary.
Rate Limiting
- Implement rate limiting to prevent abuse of your API.
By correctly setting up and managing CORS, you can securely enable cross-origin communication while protecting your application from potential threats.