Definition
Imagine you’re at a house party, and you want to borrow sugar from the neighbor next door. In the physical world, you’d just walk over and ask. But in the world of web browsers, there’s a security guard standing between every website, and by default, that guard says “no” to all cross-property requests. CORS (Cross-Origin Resource Sharing) is the system that lets the neighbor put you on an approved guest list so the security guard will let your request through.
When you visit a website, your browser enforces strict boundaries about what that website can do. Specifically, it prevents JavaScript on one website from making requests to a different website - this is called the “same-origin policy” and it’s a fundamental security feature. Without it, a malicious website could make requests to your bank using your logged-in session. But this creates a problem: legitimate applications often need to make cross-origin requests. Your frontend at app.mycompany.com might need data from an API at api.mycompany.com. CORS solves this by providing a standardized way for servers to say “I trust requests from these specific origins.”
Here’s how it works: when your browser makes a cross-origin request, it includes an Origin header saying where the request came from. The server can then respond with headers like Access-Control-Allow-Origin to indicate whether it allows that origin. If the server says yes, the browser allows the JavaScript to see the response. If not, the browser blocks it - not because the request failed, but because the browser is protecting you. The request might actually succeed on the server side, but the browser won’t let your JavaScript access the result.
Example
Real-World Scenario 1: Single Page Application Architecture
You’re building a React app hosted on www.mystore.com that needs to fetch product data from your API at api.mystore.com. Even though both are “your” domains, the browser sees them as different origins. Without CORS configured on your API server, your React app would get blocked from reading responses. The API needs to respond with Access-Control-Allow-Origin: https://www.mystore.com to allow it.
Real-World Scenario 2: Third-Party API Integration
Your website wants to display weather data from a public weather API. When your JavaScript tries to fetch from api.weather.com, the browser first checks if that API allows your origin. Public APIs typically set Access-Control-Allow-Origin: * to allow requests from any website. But if they want to restrict access to paying customers, they might only allow specific origins.
Real-World Scenario 3: Google Fonts and CDN Resources
When you include Google Fonts in your website, your browser makes requests to fonts.googleapis.com. Google’s servers include CORS headers that allow any origin to access font files. This is why you can use Google Fonts from any website - their CORS policy explicitly allows it.
Real-World Scenario 4: Preflight Requests for Complex Operations Your app needs to send a POST request with JSON data and custom headers to an API. Before sending the actual request, the browser automatically sends a “preflight” OPTIONS request asking “Is this specific type of request allowed?” The server must respond saying which methods and headers it accepts. Only then will the browser send your actual request. This two-step process happens automatically for anything beyond simple GET requests.
Analogy
The Embassy Visa System: Getting data from another origin is like needing a visa to enter another country. The same-origin policy is like requiring a visa for all foreign visitors. CORS is the system where the destination country (server) publishes a list of which countries (origins) can enter and what they can do (methods, headers). The border control (browser) checks this list before letting the request through.
The Nightclub Guest List: Imagine a nightclub (API server) where the bouncer (browser) only lets in people on the guest list. The club owner has put certain names on the approved list (allowed origins). When you (the request) show up claiming to be from a specific website, the bouncer checks the list. If you’re on it, you get in. CORS is the guest list system.
The Apartment Building Intercom: In a secure apartment building, visitors can’t just walk in. They use the intercom to call a specific apartment. The resident (server) can then buzz them in (allow the request) or not. CORS is like this system - the server decides who gets buzzed in based on where they’re coming from.
The Restaurant Private Event Room: A restaurant has a private event room. Normally, only people hosting the event can invite guests. But the restaurant can pre-authorize certain other parties to send guests. CORS headers are like this pre-authorization - the server is telling the browser “yes, it’s okay for this specific website to send requests here.”
Code Example
// CORS headers in API response
[HTTP/1.1](https://reference.apios.info/terms/http-1-1/) 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
// Preflight request (OPTIONS)
OPTIONS /api/users HTTP/1.1
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
// Server configuration (Express.js)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://myapp.com');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
next();
});
Diagram
sequenceDiagram participant Browser participant API Note over Browser,API: Simple Request Browser->>API: GET /data + Origin header API->>Browser: 200 OK + CORS headers Note over Browser,API: Preflight Request Browser->>API: OPTIONS /data API->>Browser: 204 + Allowed methods Browser->>API: POST /data API->>Browser: 201 Created
Security Notes
CRITICAL: CORS is a browser security mechanism ONLY; it does not protect against direct HTTP requests (Postman, curl, etc.).
Wildcard & Credential Misconfigurations:
- Never use Access-Control-Allow-Origin: * with credentials: This combination is rejected by browsers and is a critical misconfiguration
- Always specify exact origins: Use explicit origin lists instead of wildcards in production
- Separate policies for credentials: Use different endpoints for credentialed vs non-credentialed requests if needed
Header Security:
- Whitelist headers carefully: Only allow necessary headers with Access-Control-Allow-Headers
- Avoid exposing sensitive headers: Don’t expose Authorization, Set-Cookie, or internal headers
- Use Access-Control-Expose-Headers: Only expose headers clients actually need
- Validate Origin header: Check Origin header on server side; don’t blindly echo it back
Cache & Preflight Security:
- Limit Access-Control-Max-Age: Set reasonable cache times (3600s max); don’t cache forever
- Validate preflight requests: Properly handle OPTIONS preflight requests (don’t skip authorization)
- Don’t cache credentials: Never cache responses with Authorization headers
Scope & Limitations:
- Browser-only protection: CORS only affects browser requests; curl/Postman ignore it
- Authentication required: CORS is NOT a substitute for proper authentication/authorization
- Assume direct requests: Attackers can make direct HTTP requests without browser preflight
- Server-side validation needed: Always validate permissions server-side regardless of CORS
Common Misconfigurations:
- Echoing Origin blindly: Using
Access-Control-Allow-Origin: ${request.origin}is vulnerable to origin confusion - Overly permissive headers: Allowing all headers makes clients vulnerable to CSRF attacks
- Missing validation: Accepting all origins defeats the purpose of CORS
- Treating CORS as authentication: CORS is NOT authentication; always implement proper auth
Best Practices
- Never use Access-Control-Allow-Origin: * with credentials
- Specify exact origins instead of wildcards in production
- Validate the Origin header on the server side
- Only expose necessary headers with Access-Control-Expose-Headers
- Set appropriate Access-Control-Max-Age to cache preflight responses
- Use CORS middleware/libraries instead of manual header setting
Common Mistakes
Using Access-Control-Allow-Origin: * with credentials (fails silently), not handling OPTIONS preflight requests, exposing too many headers, setting overly long max-age for preflight cache, confusing CORS with server-side security (CORS only restricts browsers, not curl/Postman), forgetting to include Origin in Vary header.