CORS is the browser-enforced HTTP protocol through which a server allows selected cross-origin script access. It does not replace authentication, authorization, CSRF protection, or input validation, and it cannot be repaired by adding response policy headers in client code.
A sound policy uses exact origins, route-specific methods and headers, correct preflight handling, explicit credential rules, cache variation, and tests through the production proxy path.
CORS (Cross-Origin Resource Sharing) error occurs when a web application tries to access resources from a different domain, protocol, or port than the one serving the web page. This is a security feature implemented by browsers to prevent malicious websites from accessing sensitive data.
// Install cors package
// npm install cors
const express = require('express');
const cors = require('cors');
const app = express();
// [ok] Enable CORS for all routes
app.use(cors());
// [ok] Or configure specific origins
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
// Frontend code (React/Vue/Vanilla JS)
fetch('https://api.example.com/users')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
// [wrong] Error: CORS policy blocked
const express = require('express');
const app = express();
// [ok] Add CORS headers manually
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // Allow all origins
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.get('/users', (req, res) => {
res.json({ users: [] });
});
// [ok] Or use cors package (recommended)
const cors = require('cors');
app.use(cors());
// Sending cookies with request
fetch('https://api.example.com/profile', {
credentials: 'include' // Send cookies
})
.then(res => res.json())
.then(data => console.log(data));
// [wrong] Error: Credentials flag is true, but Access-Control-Allow-Credentials is not
// Node.js/Express
app.use(cors({
origin: 'http://localhost:3000', // Must specify exact origin (not *)
credentials: true // Allow credentials
}));
// Or manually
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Credentials', 'true');
next();
});
// POST request with custom header
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify({ name: 'John' })
});
// [wrong] Error: Preflight request doesn't pass access control check
// Handle OPTIONS preflight request
app.options('*', cors()); // Enable pre-flight for all routes
// Or manually
app.options('/users', (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.sendStatus(200);
});
app.post('/users', (req, res) => {
// Your POST logic
res.json({ success: true });
});
{
"name": "my-app",
"version": "1.0.0",
"proxy": "http://localhost:5000"
}
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
}
};
// app/Http/Middleware/Cors.php
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
->header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # Enable CORS for all routes
# Or specific origins
CORS(app, origins=['http://localhost:3000'])
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE");
}
};
}
}
An origin is the combination of scheme, host, and port. Two URLs are cross-origin when any of those components differ. The browser same-origin policy restricts script access to cross-origin responses, and CORS is the HTTP-header protocol through which a server opts selected origins into that access.
CORS is enforced by browsers for relevant APIs such as fetch and XMLHttpRequest. A command-line client or server-to-server request is not granted or denied by browser CORS, which is why a request can work in Postman and fail in a web page. The server configuration still owns the repair.
CORS is not authentication or authorization. A permitted origin does not identify a user, and a disallowed origin does not prevent every kind of cross-site request from reaching a server. Authenticate requests, authorize every operation, protect cookie-based state changes against CSRF, and validate input independently.
Use an exact allowlist of trusted production and development origins. Compare parsed origins, not substring or suffix tricks that accept attacker-controlled hosts. Do not reflect arbitrary Origin values into `Access-Control-Allow-Origin`.
Some cross-origin requests use the CORS-safelisted method, header, and content-type conditions and can send the actual request without a preflight. The response still needs an appropriate `Access-Control-Allow-Origin` before browser script can read it. Avoid the misleading idea that such requests bypass CORS.
Other requests trigger an OPTIONS preflight. The browser sends Origin plus `Access-Control-Request-Method` and, when needed, `Access-Control-Request-Headers`. The server answers with allowed origin, methods, and headers. Only after a successful preflight does the browser send the intended request.
Do not add CORS response headers to the client request. They are server response policy. Also avoid `mode: "no-cors"` as a supposed fix: it produces an opaque response that script generally cannot inspect and does not grant API access.
Preflight caching through `Access-Control-Max-Age` can reduce OPTIONS traffic, subject to browser limits. Policy changes may not appear instantly for cached clients, so deploy revocations carefully and use realistic cache durations. Ensure routes, proxies, and authentication middleware allow OPTIONS to reach the policy handler.
A cross-origin fetch does not include credentials by default. `credentials: "include"` asks the browser to include eligible cookies or HTTP authentication, but cookie SameSite and third-party-cookie policies still apply. The server must return `Access-Control-Allow-Credentials: true` and an explicit allowed origin.
The wildcard origin cannot be used for a credentialed response. Return the matching trusted origin and `Vary: Origin` when one response URL serves multiple origins, so shared caches do not reuse an access decision for the wrong requester. Configure CDN and reverse-proxy caching with the same rule.
JavaScript can read safelisted response headers automatically. Additional response headers require `Access-Control-Expose-Headers`. This is distinct from `Access-Control-Allow-Headers`, which answers which non-safelisted request headers may be sent.
CORS policy should be route-specific. A public immutable asset may allow every origin, while account data needs a narrow origin and credential policy. Avoid one framework switch that exposes every endpoint, method, and header simply to repair one failing request.
Start in the browser Network and Console panels. Identify whether the failure is DNS, TLS, mixed content, redirect, blocked preflight, HTTP failure, or missing CORS headers. JavaScript intentionally receives limited CORS failure detail, while developer tools show the request and policy reason.
Inspect the OPTIONS request and response separately from the actual request. Verify exact Origin, requested method and headers, status, redirects, allow headers, credentials, and Vary. A successful OPTIONS response does not help if the final response omits the allow-origin header.
Automated tests should cover each allowed origin, a deceptive disallowed origin, null origin policy where relevant, credentialed and non-credentialed requests, allowed and rejected methods and headers, preflight, final responses, errors, and cache behavior. Test through the real proxy or gateway as well as the application.
Log policy decisions server-side with a privacy-safe request ID, normalized origin, route, method, and rejection category. Do not log cookies or authorization tokens. Monitor unexpected origin volume and configuration drift after deployment.
Redirects can change origin, method handling, credentials, or preflight behavior. Configure the final API URL directly where possible and verify every redirect response and destination. A development proxy that makes requests appear same-origin can hide a production CORS defect, so keep one browser test against the deployed topology.
Reverse proxies, gateways, application servers, and CDNs can each add, remove, duplicate, or cache CORS headers. Assign one policy owner and inspect the final wire response. Multiple `Access-Control-Allow-Origin` values or duplicated middleware do not create a broader valid policy; they often make the response unusable.
Environment allowlists should be explicit configuration with reviewable changes. Do not copy localhost, preview, or wildcard settings into production. Remove retired origins, rotate preview host patterns safely, and include CORS policy in incident rollback and infrastructure tests.
CORS (Cross-Origin Resource Sharing) error occurs when a browser blocks a request from one origin (domain/port) to another origin due to security restrictions. The server must explicitly allow cross-origin requests by sending proper CORS headers.
Fix CORS by configuring your server to send Access-Control-Allow-Origin header. In Node.js, use the cors package. In development, you can use a proxy in React/Vue to avoid CORS issues.
CORS is a browser security feature. Tools like Postman or curl don't enforce CORS because they're not browsers. The error only occurs when making requests from JavaScript in a web page.
Explore 500+ free tutorials across 20+ languages and frameworks.