Skip to content
QA Knowledge Hub

CORS errors explained for testers

Updated 2026-11-13 · 2 min read

A browser may reject response access even when curl reaches the API. A local two-origin fixture demonstrates the failure, an explicit allowlist and the limits of a simple GET test.

The reproduced message

Captured in Chromium through Playwright 1.65.0, with a UI on port 4310 fetching an API on port 4311:

Access to fetch at 'http://127.0.0.1:4311/' from origin 'http://127.0.0.1:4310' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The request's JavaScript catch handler received only Failed to fetch. Inspect the browser Console and Network panel for the actual reason. Different ports are different origins, even on the same host.

1. The API does not allow the UI origin

Save this as cors-lab.cjs, run node cors-lab.cjs, and open the UI URL http://127.0.0.1:4310 in a browser. The fixture and its header fix were both run locally. It intentionally allows one origin and does not implement authentication.

const http = require('node:http');
const allowed = 'http://127.0.0.1:4310';
http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.end(`<script>
    fetch('http://127.0.0.1:4311/').then(r => r.json())
      .then(data => console.log(data)).catch(console.error);
  </script>`);
}).listen(4310, '127.0.0.1');
http.createServer((req, res) => {
  if (req.headers.origin === allowed) {
    res.setHeader('Access-Control-Allow-Origin', allowed);
  }
  res.setHeader('Vary', 'Origin');
  res.setHeader('Content-Type', 'application/json');
  res.end(JSON.stringify({ ok: true }));
}).listen(4311, '127.0.0.1');

Remove the allow-origin header to reproduce the error; restore it to let the browser read the JSON. mode: 'no-cors' does not give JavaScript a readable API response.

2. The preflight is rejected

JSON writes and custom request headers can trigger an OPTIONS preflight. Inspect it separately from the intended request. Configure the API or gateway to allow the intended origin, method and request headers. This probe was run against the local fixture; its missing allow-methods and allow-headers entries show that the simple-GET fixture is not a preflight implementation.

curl -i -X OPTIONS http://127.0.0.1:4311/   -H 'Origin: http://127.0.0.1:4310'   -H 'Access-Control-Request-Method: POST'   -H 'Access-Control-Request-Headers: content-type'

3. Credential settings or an upstream failure differ

For credentialed cross-origin requests, use an explicit permitted origin and the appropriate allow-credentials response; a wildcard origin is not valid for that mode. Cookies also have their own SameSite and Secure rules. A TLS failure, redirect or gateway error can prevent the expected CORS headers from appearing; inspect that earlier failure too.

Prevention and interview answer

Test allowed and denied origins, preflights and error responses in a real browser. Never reflect every incoming Origin while enabling credentials. “A passing API-client call checks connectivity; I separately verify that the browser can read the response under the intended origin policy.”

For certificate failures before HTTP, see SSL handshake errors.

Quick answers

Why does curl work while browser fetch fails?

curl does not enforce browser CORS response access rules. The browser also sends an Origin header and may perform a preflight.

Does CORS replace authentication?

No. It controls browser cross-origin response access, not whether arbitrary clients can call the API.

Sources

Want a project you can show in interviews?

The portfolio project kit walks you through testing a deliberately buggy app with Selenium or Playwright and API tests, then explaining it. The app itself is free.