Security

CSP vs CORS: why your fetch is blocked

A fetch() fails in the console with something about a policy, and it isn't obvious whether the browser is enforcing CORS or Content-Security-Policy. They're separate checks with separate fixes, and if you've just added a CORS proxy, the CSP one has a trap: you have to allowlist the proxy, not the API.

· ~5 min read · Updated Oct 13, 2026

The proxy's origin must be in connect-src

Say you route a call through corsproxy.dev because the upstream API sends no Access-Control-Allow-Origin header, and your page also ships a locked-down CSP. The request now fails with a CSP violation instead of a CORS error, and the instinct is to add the upstream API to connect-src. That doesn't help.

connect-src governs which hosts the page's own fetch(), XMLHttpRequest, WebSocket, and EventSource calls may connect to. With a proxy, your page only ever connects to api.corsproxy.dev. The second hop, from the proxy to the upstream, happens server-side, where your page's CSP doesn't apply. So the proxy's origin is the one to allowlist:

// Page CSP needs: connect-src 'self' https://api.corsproxy.dev
// api.example.com does NOT need to be in connect-src: the browser
// never talks to it directly.
const target = encodeURIComponent('https://api.example.com/users');
fetch(`https://api.corsproxy.dev/proxy?url=${target}&key=YOUR_CORSPROXY_KEY`)
  .then(r => r.json())
  .then(data => console.log(data));

Two consequences worth knowing:

Because one entry now opens a path to any host the proxy will fetch, it's worth limiting what your key can reach on the proxy side too, for example with allowed_targets (see the API docs).

Telling the two errors apart

The console text gives it away. A CORS error mentions the response and Access-Control-Allow-Origin:

Access to fetch at 'https://api.example.com/users' from origin
'https://app.mysite.com' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested resource.

A CSP violation says "Content Security Policy directive" and names the directive (usually connect-src, or the default-src it fell back to):

Refused to connect to 'https://api.example.com/users' because it
violates the following Content Security Policy directive:
"connect-src 'self'".

If you see "Content Security Policy directive" anywhere in the message, changing CORS settings on the target server won't help, because the request never reached it.

  CORS CSP (connect-src)
Who sets the ruleThe target serverYour own page
Where it's checkedOn the responseBefore the request is sent
Fixed byServer config, or a proxyYour own CSP header
Console mentionsAccess-Control-Allow-Origin"Content Security Policy directive"

CSP is set by your own page, via a Content-Security-Policy response header or a <meta http-equiv="Content-Security-Policy"> tag. CORS is decided by headers the server you're calling sends back; the mechanics are in the CORS glossary entry and Why am I getting a CORS error?. Good CORS headers don't get you past connect-src, and a permissive CSP doesn't get you past CORS. A request has to clear both.

Fixing a CSP block

Add the host you're calling to connect-src in whatever sets your CSP header. In Express, for example:

app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; connect-src 'self' https://api.corsproxy.dev https://api.example.com"
  );
  next();
});

A <meta http-equiv="Content-Security-Policy"> tag works too, but a real response header is better: the meta tag can't set every directive (notably frame-ancestors and report-uri), and it only applies from the point it appears in the document.

Don't fix this by deleting connect-src or widening it to *. The directive limits where data can be sent if something on the page is later compromised, so list the specific hosts you actually call.

Try it

Create a free key, then add api.corsproxy.dev to your connect-src.