No 'Access-Control-Allow-Origin' Header (CORS)
No 'Access-Control-Allow-Origin' header? Fix it in 3 checks: failed preflight, exact origin echoed back, and credentials mode. Free instant check, no sign-up.
Check your domain for this issue now
Free, no sign-up. Runs the exact check this guide describes and shows what to fix.
Problem
The browser console shows: Access to fetch at '...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Symptoms
- The request works in
curlor Postman but fails in the browser. - The server logs a
200for the request, yet the page reports an error and gets no data. - A mysterious
OPTIONSrequest appears in the Network tab right before the call that fails.
What this error actually means
CORS is enforced by the browser, not the server. It is defined in the WHATWG Fetch standard, and it is a relaxation of the same-origin policy, not a security feature your server turns on. So this is the part that trips everyone up: the response almost always arrives. Your server handled the request, returned 200, and sent the body. The browser received all of it — and then, seeing no Access-Control-Allow-Origin header that names the page’s origin, refused to let your JavaScript read it.
That’s why curl succeeds and the page doesn’t. curl has no same-origin policy to enforce. The fix is never on the client; it is always a response header the server has to add.
Top 3 Causes
- The server simply never sends the header (or sends the wrong origin) - The most common case. The response has no
Access-Control-Allow-Originat all, or it names a different origin than the page that made the call. The browser needs the value to either be*or exactly match the requesting origin. - A preflight
OPTIONSrequest failed - Anything beyond a “simple” request triggers a preflight. Simple means GET/HEAD/POST with only safelisted headers and aContent-Typeofapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain. The moment you send a JSON body, aPUT/DELETE/PATCH, or a header likeAuthorization, the browser first sends anOPTIONScarryingAccess-Control-Request-MethodandAccess-Control-Request-Headers. If the server doesn’t answer that withAccess-Control-Allow-Origin,-Methods,-Headersand a 2xx, the real request is never sent. - Credentials clash with the wildcard - When the request sends cookies or auth (
credentials: 'include'),Access-Control-Allow-Origin: *is rejected. The server must echo the exact origin and addAccess-Control-Allow-Credentials: true.
Diagnose with DechoNet
- HTTP Check to see the actual response headers the server returns — confirm whether
Access-Control-Allow-Originis present and what value it carries. - DNS Lookup to verify the API hostname resolves to the environment you think it does; a missing header is often a stale or wrong backend, not a code change.
Resolution Checklist
- Read the response headers and confirm whether
Access-Control-Allow-Originis present at all. - If present, confirm it is
*or an exact, character-for-character match of the requesting origin (scheme + host + port). - If an
OPTIONSprecedes the failing call, make the server answer it withAccess-Control-Allow-Origin,Access-Control-Allow-Methods, andAccess-Control-Allow-Headers, returning a 2xx. - For credentialed requests, drop the wildcard: echo the specific origin and send
Access-Control-Allow-Credentials: true. - When echoing the origin, add
Vary: Originso a shared cache doesn’t serve one origin’s allow-header to another. - Add
Access-Control-Max-Ageto let the browser cache the preflight result and stop re-asking on every call.
When to Escalate
- If the headers are correct at the application but still missing in the browser, a CDN, reverse proxy, or WAF in front of the origin is likely stripping them — take it to whoever owns that layer.
- If only credentialed requests fail, audit every hop for a hard-coded
Access-Control-Allow-Origin: *; a single wildcard anywhere in the chain breaks the credentialed path.
Related Tools
Related Guides
Share this guide