Free Tool
CORS Checker
Test your website's Cross-Origin Resource Sharing policy. Detect wildcard misconfigurations, credential exposure, and origin reflection bugs that could allow any website to read your authenticated API responses.
Sign in with Google or GitHub to run the scan. Your first scan is free — no credit card required.
ZeriFlow Data — 12,400+ sites analyzed
Among APIs and web applications scanned on ZeriFlow, 27% use Access-Control-Allow-Origin: * with credentials-bearing endpoints — a configuration that exposes authenticated data to any website.
What is CORS and Why Does It Exist?
CORS is a browser security mechanism that restricts which web pages can make requests to a different domain. Without it, malicious.com could fetch data from your-bank.com using your session cookies.
The same-origin policy is the default: https://app.example.com cannot make requests to https://api.other.com. CORS is the controlled way to relax this restriction when you legitimately need cross-origin communication — for example, a frontend on Vercel calling an API on Render.
The CORS Headers Explained
Access-Control-Allow-OriginWhich origins can access the resource. * means any origin — dangerous with credentials.
Access-Control-Allow-MethodsWhich HTTP methods are allowed: GET, POST, PUT, DELETE, OPTIONS.
Access-Control-Allow-HeadersWhich request headers the client is permitted to send (e.g. Authorization, Content-Type).
Access-Control-Allow-CredentialsWhether cookies and Authorization headers are included in the request. Setting true with * is blocked by browsers.
Access-Control-Max-AgeHow long the preflight response can be cached in seconds. Higher values reduce OPTIONS request overhead.
The Dangerous CORS Misconfigurations
1. Wildcard with credentials
Browsers block this combination, but servers that emit both headers indicate a deeper misunderstanding of the CORS model — and the intent to expose authenticated data to all origins is clearly present.
# WRONG — browsers block this but the misconfiguration is a signal
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true2. Reflecting the Origin header without validation
Some servers copy whatever origin is in the request header directly into the response. This is functionally equivalent to * but bypasses some security checks.
// WRONG — any origin gets access res.setHeader('Access-Control-Allow-Origin', req.headers.origin); // CORRECT — validate against an explicit allowlist const ALLOWED = new Set(['https://app.example.com']); if (ALLOWED.has(req.headers.origin)) { res.setHeader('Access-Control-Allow-Origin', req.headers.origin); }
3. Regex validation bugs
Overly broad regex matches silently allow unintended origins. The example below is intended to allow only example.com but also matches evil.example.com and notexample.com.
# BAD — matches evil.example.com and notexample.com if (origin.match(/example\.com/)) allow(); # CORRECT — anchored full-origin match if (origin.match(/^https:\/\/example\.com$/)) allow();
How to Configure CORS Correctly
Express (cors package)
import cors from 'cors';
const ALLOWED_ORIGINS = [
'https://app.example.com',
'https://www.example.com',
];
app.use(cors({
origin: (origin, cb) => {
if (!origin || ALLOWED_ORIGINS.includes(origin)) {
cb(null, true);
} else {
cb(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));FastAPI (CORSMiddleware)
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://app.example.com",
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)Nginx
map $http_origin $cors_origin {
default "";
"https://app.example.com" $http_origin;
}
server {
add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Credentials true;
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Max-Age 86400;
return 204;
}
}Next.js (Route Handler)
const ALLOWED = new Set(['https://app.example.com']);
export async function GET(req: Request) {
const origin = req.headers.get('origin') ?? '';
const headers = new Headers();
if (ALLOWED.has(origin)) {
headers.set('Access-Control-Allow-Origin', origin);
headers.set('Access-Control-Allow-Credentials', 'true');
}
return new Response(JSON.stringify(data), { headers });
}Frequently Asked Questions
What is CORS and why do I need it?
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which web pages can make HTTP requests to a different domain. The browser's same-origin policy blocks cross-origin requests by default — without it, a malicious page could silently make requests to your bank using your session cookie. CORS is the controlled way to selectively relax this restriction for legitimate use cases, like a frontend on Vercel calling an API on Render.
What does Access-Control-Allow-Origin: * mean?
Access-Control-Allow-Origin: * allows any origin to make cross-origin requests to that endpoint. For public, unauthenticated resources like a public CDN, this is acceptable. For any endpoint that uses cookies, session tokens, or returns user-specific data, this is dangerous — it allows any website to read the response. Critically, browsers block the combination of * and Access-Control-Allow-Credentials: true, but servers that emit both indicate deeper misconfiguration.
Why am I getting a CORS error in my browser?
A CORS error means the server did not return the correct Access-Control-Allow-Origin header for the origin making the request. Common causes include: the server only allows production domains but the request comes from localhost during development; the server returns CORS headers on GET but not on OPTIONS preflight requests; or a proxy layer strips the CORS headers. The fix is always server-side — adding the requesting origin to the allowed list or fixing the OPTIONS response, never by disabling CORS enforcement in the browser.
Can CORS be exploited even with a whitelist?
Yes — through validation bugs. A common mistake is using a regex like /example\.com/ which matches evil-example.com. Another is reflecting the Origin header without validation (if the request has an Origin, copy it directly into Access-Control-Allow-Origin), which is equivalent to a wildcard but harder to spot. Null origin exploitation is also a known attack: some servers allowlist the string 'null', which is sent by sandboxed iframes and local file requests — attackers can use this to trigger requests with a null origin.
What is a CORS preflight request?
Before sending certain cross-origin requests (those with custom headers, non-simple methods like PUT or DELETE, or non-standard content types), the browser first sends an HTTP OPTIONS request to ask the server whether the actual request is allowed. This is the preflight. The server must respond with Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers headers. If the preflight fails, the browser never sends the actual request. This is why CORS errors often appear as OPTIONS requests in the network tab.