CORS errors are frustrating because the browser often shows a scary message, but the real cause is usually a small missing header.

You may see something like this in the browser console:

Access to fetch at 'https://api.example.com/data' from origin 'https://app.example.com'
has been blocked by CORS policy.

The message sounds complicated. In plain language, the browser is saying:

“This website is trying to read data from another website. I will only allow it if the API explicitly says this website is allowed.”

The CORS Checker helps you test that permission step.

What CORS is, in simple terms

CORS stands for Cross-Origin Resource Sharing.

An origin is the combination of:

  • protocol, such as https;
  • domain, such as app.example.com;
  • port, such as 443 or 3000.

These are different origins:

https://app.example.com
https://api.example.com
http://app.example.com
http://localhost:3000
http://localhost:5173

Even if the domains look similar, the browser treats them as different origins.

CORS is a browser safety rule. It does not stop your server from receiving the request. It stops JavaScript in the browser from reading the response unless the API allows it.

The common beginner mistake

When people see a CORS error, they often think:

“The API is down.”

But that is not always true.

The API might be working. The request might even reach the API. The browser is only blocking the frontend from reading the response because the response does not have the right CORS headers.

That difference matters.

Step 1: identify the frontend origin

First, write down where your frontend is running.

Examples:

http://localhost:5173
https://app.example.com
https://admin.example.com

This is the origin the API must allow.

Be exact. These are not the same:

http://localhost:3000
http://localhost:5173
https://localhost:5173

Different port means different origin. Different protocol means different origin.

Step 2: identify the API URL

Next, write down the exact API URL being called.

Example:

https://api.example.com/v1/users

Do not only check the base domain. CORS behaviour can differ by route, method, or server layer.

For example:

https://api.example.com/v1/users
https://api.example.com/v1/admin/users

may not be handled by the same backend code.

Step 3: open the CORS Checker

Open CORS Checker and enter the API URL.

The tool will try to make a browser request and show what the browser can see. This is important because CORS is enforced by the browser. Testing from a terminal with curl is useful, but it does not behave exactly like browser JavaScript.

If the request is blocked, the checker helps confirm that the browser cannot read the response.

Step 4: look for Access-Control-Allow-Origin

The most important header is:

Access-Control-Allow-Origin: https://app.example.com

or sometimes:

Access-Control-Allow-Origin: *

This tells the browser which origin is allowed to read the response.

If your frontend is running at:

https://app.example.com

then this is good:

Access-Control-Allow-Origin: https://app.example.com

This is not good enough:

Access-Control-Allow-Origin: https://www.example.com

The browser needs an exact match, unless the API uses * for public non-credential requests.

Step 5: check whether credentials are involved

Credentials usually means cookies, HTTP auth, or requests with credentials: "include".

If the frontend sends credentials, the API cannot use this:

Access-Control-Allow-Origin: *

It must return the specific origin:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

This is a common source of confusion. * feels like it should allow everything, but browsers reject it for credentialed requests.

Step 6: understand preflight requests

Some requests trigger a preflight request. A preflight is the browser asking for permission before sending the real request.

The browser sends an OPTIONS request first when the real request is not considered simple. This can happen when you use:

  • methods like PUT, PATCH, or DELETE;
  • custom headers like Authorization;
  • content types like application/json in some cases.

The API must answer the preflight with headers such as:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type

If the preflight fails, the real request may never be sent by the browser.

Step 7: check allowed methods

If your frontend sends:

DELETE /v1/users/123

then the preflight response must allow DELETE:

Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS

If the server only allows:

Access-Control-Allow-Methods: GET, POST

then the browser will block the request.

Step 8: check allowed headers

If your frontend sends an Authorization header:

Authorization: Bearer <token>

then the preflight response must allow it:

Access-Control-Allow-Headers: Authorization, Content-Type

If Authorization is missing from Access-Control-Allow-Headers, the browser may block the request before your API handler sees it.

This is why login-protected APIs often fail CORS during development.

Step 9: separate CORS errors from API errors

These are different problems:

SymptomMeaning
CORS blockedBrowser refused to expose the response.
401 UnauthorizedAPI read the request but rejected authentication.
403 ForbiddenAPI understood who you are but denied access.
404 Not FoundThe route was not found.
500 Internal Server ErrorThe server crashed or failed.

Sometimes the API returns 500, but the browser only shows a CORS error because the error response did not include CORS headers. That makes debugging harder.

A good API should include CORS headers even on error responses.

Step 10: compare browser test with curl

If the browser fails but curl works, that does not automatically mean the browser is wrong.

curl is not restricted by CORS. It can show that the API exists, but it cannot prove that browser JavaScript is allowed to read it.

Use curl to check server behaviour. Use the browser or the CORS Checker to check browser permission.

Common fixes

On the API side, the fix is usually one of these:

  • add the frontend origin to the allowed origins list;
  • allow the HTTP method being used;
  • allow headers such as Authorization and Content-Type;
  • return CORS headers for OPTIONS preflight requests;
  • return CORS headers on error responses too;
  • avoid Access-Control-Allow-Origin: * when using credentials.

The exact configuration depends on your backend, framework, proxy, and hosting setup.

My CORS checklist

When I see a CORS error, I check:

  1. What is the frontend origin exactly?
  2. What is the exact API URL?
  3. Is the browser sending credentials?
  4. Is there a preflight OPTIONS request?
  5. Does the response include Access-Control-Allow-Origin?
  6. Does the allowed origin exactly match the frontend origin?
  7. Are the method and headers allowed?
  8. Do error responses also include CORS headers?

CORS feels mysterious until you remember the simple rule:

The browser will only let frontend JavaScript read a cross-origin response if the API response says that origin is allowed.

The CORS Checker is just a faster way to see that decision without guessing.

Comments

Comments are welcome — please read the comment policy first. Powered by giscus and GitHub Discussions.