Home › Web & HTTP

has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource — how to fix

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Quick fix: The server must send Access-Control-Allow-Origin: http://localhost:3000 (your origin). If you don't control the server, call it from your backend or a dev-server proxy instead of the browser. mode: 'no-cors' will not give you the data.

What it means

Browsers block JavaScript from reading responses from a different origin (scheme + host + port) unless that server explicitly allows it with CORS headers. localhost:3000 → localhost:8000 counts as a different origin too.

The request often did reach the server; the browser just refuses to hand the response to your code. That's why it works in Postman or curl but not in the browser.

Fix on the server

The response must include your origin:

Access-Control-Allow-Origin: http://localhost:3000

Express:

import cors from 'cors';
app.use(cors({ origin: ['http://localhost:3000', 'https://myapp.com'] }));

Flask: pip install flask-cors, then CORS(app, origins=["http://localhost:3000"]).

FastAPI:

from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:3000"], allow_methods=["*"], allow_headers=["*"])

Django: django-cors-headers with CORS_ALLOWED_ORIGINS = ["http://localhost:3000"].

nginx: add_header Access-Control-Allow-Origin "https://myapp.com" always;

Preflight requests

Requests with JSON bodies, custom headers (like Authorization) or methods like PUT/DELETE trigger an OPTIONS preflight first. The server must answer it with 2xx and:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

If your error mentions "Response to preflight request doesn't pass access control check", the OPTIONS route is missing or returns an error/redirect.

Cookies and credentials

With fetch(url, { credentials: 'include' }), the server must send Access-Control-Allow-Credentials: true, and Access-Control-Allow-Origin must be the exact origin, not *.

You don't control the server

Call the API from your own backend, or use a dev-server proxy so the browser only talks to your origin:

// vite.config.js
export default { server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true } } } };

Then fetch('/api/data').

Why mode: 'no-cors' doesn't help

It returns an opaque response: status 0, empty body. Your code still can't read the data.

Misleading CORS errors

If the server crashes (500) or the URL is wrong (404), error responses often lack CORS headers, so the browser reports CORS instead of the real problem. Check the server logs and the Network tab's status code.

Last updated 2026-10-27.