CORS errors explained, and how to fix them
By TabBenchHow we check our guides
“Access to fetch at … has been blocked by CORS policy” is one of the most searched error messages in web development, and one of the most misunderstood. The request usually reached the server and got an answer; the browser then refused to hand that answer to your JavaScript because the server did not say your site was allowed to read it.
CORS is a browser rule, enforced on the client but fixed on the server. This guide explains what triggers it, how to read the error, and exactly which headers to send, including the traps with credentials, preflight requests and caching.
Step by step
Understand the same-origin policy
An origin is the combination of scheme, host and port. By default, a page on https://app.example.com may not read responses from https://api.example.com, because they are different origins. Cross-Origin Resource Sharing is the opt-in mechanism: the other server names the origins that may read its responses using Access-Control-Allow-Origin.
Tell simple requests from preflighted ones
A GET, HEAD or plain form POST with only basic headers is sent straight away, and the browser checks the response headers afterwards. Anything else (PUT, PATCH, DELETE, a JSON Content-Type, or a custom header such as Authorization) makes the browser send an OPTIONS preflight first, asking whether the real request is allowed. If the preflight fails, the real request is never sent.
Read the error message
“No Access-Control-Allow-Origin header is present” means the response did not include the header. “The value of the Access-Control-Allow-Origin header must not be the wildcard * when credentials mode is include” means you send cookies, so the server must name the exact origin. “Request header field … is not allowed by Access-Control-Allow-Headers” and “Method … is not allowed” refer to the preflight answer. “Response to preflight request doesn't pass access control check” often means the OPTIONS request was redirected, needed authentication or returned an error status.
Send the right headers from the server
On the real response add Access-Control-Allow-Origin with your exact origin, and Vary: Origin if the value depends on the request. For preflights, answer OPTIONS with a 204 status and Access-Control-Allow-Methods, Access-Control-Allow-Headers and optionally Access-Control-Max-Age. If the page sends cookies or an Authorization header with credentials, add Access-Control-Allow-Credentials: true. The HTTP Header Generator builds these and outputs them for nginx, Apache, Express and other servers.
Test with the response in front of you
Open the Network tab and look at both the OPTIONS request and the real one. Paste the response headers into the HTTP Header Analyzer, which checks for the wildcard-with-credentials mistake and a missing Vary: Origin. Retest after every change, and clear the browser's cached preflight if needed.
Things worth knowing
- Do not fix CORS with a public “CORS proxy” or by disabling web security in your browser. Both hide the problem and expose your users.
- CORS does not protect your server from other clients: curl and other servers ignore it. It protects users' browsers.
- The wildcard * cannot be combined with credentials, and * in Access-Control-Allow-Headers is treated literally when credentials are used.
- If the allowed origin is chosen dynamically, check it against a fixed allow-list and always add Vary: Origin.
Frequently asked questions
Why does my request work in Postman but not in the browser?
Why is there an OPTIONS request I never made?
Can I set Access-Control-Allow-Origin to *?
Do I need CORS for localhost development?
Related tools
HTTP Header Analyzer
Paste HTTP headers to see what each means and what to fix.
Content-Type Header Checker
Pick, check or detect the right Content-Type header.
HTTP Method Reference
GET, POST, PUT, PATCH, DELETE: safe, idempotent, cacheable.
MIME Type Lookup
Find the MIME type for any file extension, and back.