has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. means your ASP.NET Core API answered the request, but without the header that tells the browser this page may read the answer. Register a CORS policy that names the page's origin and apply it with UseCors.

In Program.cs, call AddCors with a named policy listing the exact origin (scheme, host and port, no trailing slash), then app.UseCors("PolicyName"). The JavaScript does not change.

The error

Access to fetch at 'http://127.0.0.1:15601/api/patients' from origin 'http://127.0.0.1:15602' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Failed to load resource: net::ERR_FAILED

Those two lines are the browser console; the page itself showed only Error: TypeError: Failed to fetch, because the browser keeps the reason from JavaScript.

The page was served from http://127.0.0.1:15602 and called an API on http://127.0.0.1:15601. A different port is a different origin.

Why it happens

CORS is enforced by the browser, not by the server. Asking the API the way the browser does, with an Origin header, shows that it worked fine:

curl.exe -s -i -H 'Origin: http://127.0.0.1:15602' http://127.0.0.1:15601/api/patients
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Fri, 23 Oct 2026 04:39:45 GMT
Server: Kestrel
Transfer-Encoding: chunked

[{"id":1,"name":"Maria Garcia"},{"id":2,"name":"David Chen"}]

A 200 with both patients, and no Access-Control-Allow-Origin header. The browser gets the same response, sees that it does not name the calling page's origin, and withholds it from the script. That is the whole mechanism: the request ran on the server, only the reading was blocked. A request with a custom header such as Authorization makes the browser send an OPTIONS preflight first; against this API the preflight got HTTP/1.1 405 Method Not Allowed, because the route accepts only GET and nothing answers OPTIONS without the CORS middleware.

The fix

builder.Services.AddCors(options =>
{
    options.AddPolicy("ClinicPage", policy =>
        policy.WithOrigins("http://127.0.0.1:15604")
              .WithMethods("GET"));
});

var app = builder.Build();

app.UseCors("ClinicPage");

The fixed API on port 15603, asked from the fixed page's origin:

curl.exe -s -i -H 'Origin: http://127.0.0.1:15604' http://127.0.0.1:15603/api/patients
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Fri, 23 Oct 2026 04:39:45 GMT
Server: Kestrel
Access-Control-Allow-Origin: http://127.0.0.1:15604
Transfer-Encoding: chunked

The preflight now returns 204 No Content with Access-Control-Allow-Methods: GET and the same origin header. An origin the policy does not list still gets a 200 without the header, so only the named page can read the data.

Order matters in a pipeline that has routing and authorization. Microsoft's CORS page puts it this way: "The call to UseCors must be placed after UseRouting, but before UseAuthorization." This minimal API calls neither, so UseCors goes right after Build(); a test copy with UseCors after MapGet also sent the header, because minimal API endpoints run at the end of the pipeline.

Two classic mistakes. AllowAnyOrigin() with AllowCredentials() did not give a bad response, as the documentation suggests; the app refused to start with System.InvalidOperationException: The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time. List the origins instead. And WithOrigins("http://127.0.0.1:15604/"), with a trailing slash, started fine but never matched: the GET and the preflight both came back without Access-Control-Allow-Origin, which is the original error again.

How it was reproduced

Four empty projects from dotnet new web on .NET SDK 10.0.402 with the ASP.NET Core 10.0.13 runtime, no packages: an API without CORS on 15601, a static page calling it on 15602, the fixed API on 15603 and its page on 15604, all bound to 127.0.0.1. The server side was checked with curl.exe 8.21.0, sending the Origin header a browser would send; the console lines come from a Chromium-based browser opening the page on 15602, and the same browser on the fixed page on 15604 printed no error and showed both patients.

Frequently asked

How do I fix No Access-Control-Allow-Origin header in ASP.NET Core?
Add builder.Services.AddCors with a named policy that lists the calling page's origin in WithOrigins, then call app.UseCors with that policy name. The API then returns Access-Control-Allow-Origin for that origin and the browser lets the script read the response.
Why does my API work in Postman or curl but not in the browser?
CORS is enforced only by browsers. The server answers every client the same way; the browser alone checks for Access-Control-Allow-Origin and hides the response from the page when the header is missing.
Can I use AllowAnyOrigin with AllowCredentials in ASP.NET Core?
No. On ASP.NET Core 10 the app throws InvalidOperationException at startup saying the CORS protocol does not allow a wildcard origin with credentials. List the allowed origins with WithOrigins instead.

More decoded errors in the Fixes category. For a design that needs no CORS at all, because no browser calls the API directly, see The App Needs a Door: A Thin API Over the Clinic.