Few pairs of security terms get confused as often as these two. They look alike, they both involve requests between different origins, and people routinely reach for one when they mean the other. But they are not two versions of the same thing. One is a browser permission system you configure; the other is an attack you have to defend against. Getting them straight is the first step to handling both correctly.
| CORS | CSRF | |
|---|---|---|
| What it is | A browser permission system | An attack |
| Who configures / does it | You configure it on your server | An attacker exploits it |
| What it protects | Your API responses from being read by other origins | Your server actions from being triggered by other sites |
| Browser behavior | Blocks script from reading cross-origin responses | Sends the request anyway, attaches cookies automatically |
| Fix | Access-Control-Allow-Origin headers on the server | sameSite cookies + CSRF token on state-changing routes |
| Cookie-based auth needed? | No | Yes — CSRF targets cookie sessions |
| Bearer-token APIs vulnerable? | Still need CORS configured | No — bearer tokens are not auto-attached by the browser |
CORS: a permission system
Cross-Origin Resource Sharing is a rule the browser enforces about which other origins are allowed to read responses from your API. By default the browser stops a page on one origin from reading responses from another, and CORS is how your server grants exceptions, for example letting your own frontend on one domain call your API on another. It is a setting. The danger is setting it too wide, like a wildcard origin with credentials, which lets any site read your API as your user. Details in CORS misconfiguration.
Scan your own app for issues like these
Paste your live URL. We check what your app serves publicly for exposed keys and misconfigurations. No account, no install.
CSRF: an attack
Cross-Site Request Forgery is not a setting; it is a thing an attacker does. Another site causes a logged-in user's browser to send a request to your app, and because the browser attaches the session cookie automatically, your server treats it as legitimate and acts on it, changing an email, making a transfer. You defend against it with sameSite cookies and a CSRF token. Details in the broader picture and specifically in what CSRF is.
The trap: CORS does not stop CSRF
Here is the misunderstanding that causes real bugs. People assume a strict CORS policy protects them from CSRF. It does not. CORS controls whether a script can read your response. A CSRF attack does not care about reading the response; it only needs the request to happen and have an effect. The browser still sends it, your server still acts on it, and the attacker never needed to read anything. So you cannot rely on CORS for CSRF protection; they solve different problems.
What each one needs
- CORS: allowlist the specific origins your own frontend uses; never pair a wildcard origin with credentials. Fix for a CORS wildcard.
- CSRF: if you use cookie-based auth, set sameSite and require a CSRF token on state-changing requests. Fix for missing CSRF protection.
CORS configuration — correct vs dangerous
// Express — correct: allowlist your own origins only
const cors = require('cors');
app.use(cors({
origin: ['https://yourapp.com', 'https://www.yourapp.com'],
credentials: true, // only if you need cookies cross-origin
}));
// DANGEROUS: wildcard with credentials lets any site read your API as the user
app.use(cors({
origin: '*', // never pair this with...
credentials: true, // ...this — browsers block it, but the intent is wrong
}));
// Next.js route handler — CORS headers
export async function GET() {
return new Response(JSON.stringify(data), {
headers: {
'Access-Control-Allow-Origin': 'https://yourapp.com', // not '*'
'Content-Type': 'application/json',
},
});
}CSRF protection — sameSite cookies and tokens
// Set-Cookie with sameSite — prevents the browser sending the cookie cross-origin
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax
// Express with csurf (or csrf package) — token on every state-changing route
const csrf = require('csrf');
const tokens = new csrf();
app.post('/change-email', (req, res) => {
// Verify the CSRF token from the request header or body
if (!tokens.verify(req.session.csrfSecret, req.body._csrf)) {
return res.status(403).send('Bad token');
}
// safe to proceed
});
// Next.js App Router — Edge Middleware checking the Origin header
export function middleware(req) {
const origin = req.headers.get('origin');
const allowed = ['https://yourapp.com'];
if (req.method !== 'GET' && !allowed.includes(origin)) {
return new Response('Forbidden', { status: 403 });
}
}“Blocked by CORS policy” — what the browser error means
When you see Access to fetch at 'https://api.example.com/' from origin 'https://app.example.com' has been blocked by CORS policy, the browser is stopping your frontend from reading a response from a different origin because the server did not send an Access-Control-Allow-Origin header that permits it. This is not an attack — it is a missing permission. The fix is always server-side: add the right header or configure CORS middleware. Never disable CORS checking in the browser to work around it; that removes the protection entirely.
Frequently asked questions
What is the one-sentence difference between CORS and CSRF?
CORS is a browser rule about which other origins are allowed to read responses from your API; CSRF is an attack where another site makes a logged-in user's browser send a request to your app. CORS is a permission system you configure; CSRF is a threat you defend against. They both involve cross-origin requests, which is why they get confused, but one is a setting and the other is an attack.
Does a strict CORS policy protect me from CSRF?
No, and this is the most common misunderstanding. CORS controls whether a script can read your response, but a CSRF attack does not need to read the response; it just needs the request to happen and have an effect, like changing an email. The browser still sends the request and your server still acts on it. So a locked-down CORS policy does not stop CSRF; you need sameSite cookies and a CSRF token for that.
What does 'blocked by CORS policy' mean?
The browser is refusing to let your frontend script read the server's response because the server did not send the right Access-Control-Allow-Origin header. This is the browser protecting you: your JavaScript on one origin tried to read a response from another origin, and the server did not explicitly allow it. The fix is to add the correct CORS header on the server, not to disable security in the browser. It is not an attack — it is a missing permission.
Which one do I actually need to worry about?
Both, for different reasons. Misconfigured CORS — especially a wildcard origin with credentials — lets other sites read your API responses as your user. Missing CSRF protection lets other sites trigger state-changing actions as your user. If your app uses cookie-based auth, you need CSRF defenses. If it exposes an API meant only for your own frontend, you need a correct CORS policy. Most apps need both handled.
Do bearer-token APIs need CSRF protection?
Generally no. CSRF relies on the browser automatically attaching credentials, which is how cookies work. An API authenticated with a bearer token in an Authorization header is not sent automatically by the browser — the attacker's page cannot include it — so the classic CSRF attack does not apply. You still need a correct CORS policy, but a CSRF token is a cookie-auth concern.
What is sameSite and how does it help with CSRF?
The sameSite cookie attribute controls when the browser attaches a cookie to a cross-origin request. With sameSite=Strict, the cookie is never sent on a cross-origin request. With sameSite=Lax (the modern browser default), it is only sent on top-level navigation like clicking a link, not on fetch or XHR from another site. Either setting prevents the browser from automatically attaching your session cookie when an attacker's page makes a request to your app — which is the mechanism CSRF depends on.
Check both in your app
A scan flags a permissive CORS policy in what your app serves, and reviewing for CSRF is part of securing a cookie-authenticated app. Run a free scan and see how your app handles cross-origin access.