jsonerrorscorsapijavascriptfetch

CORS Error Fetching JSON from API — How to Fix It

·7 min read

What Is a CORS Error?

CORS stands for Cross-Origin Resource Sharing. A CORS error means your browser blocked a fetch request because the server did not give permission for your origin (domain + port) to read its response.

The browser console shows something like:

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.

CORS is a browser security feature — it does not affect curl, Postman, or server-to-server requests. Only browser-based JavaScript fetch calls are blocked.

Why CORS Exists

Without CORS, any website you visit could silently read your banking data, email, or private API responses using your login cookies. CORS lets servers say "only my own frontend is allowed to read responses from me."

The Four CORS Scenarios

1. Simple request blocked — GET or POST with simple headers, server returns no CORS headers at all.

2. Preflight blocked — The browser sends an OPTIONS request first (called a preflight) for any request with custom headers, PUT, DELETE, or PATCH methods, or Content-Type: application/json. If the server does not handle OPTIONS correctly, the actual request is never sent.

3. Credentials blocked — You send cookies or Authorization headers but the server's CORS config does not allow credentials.

4. Wildcard + credentials — The server returns `Access-Control-Allow-Origin: *` but you are sending credentials. The browser requires a specific origin, not a wildcard, when credentials are involved.

Fix on the Server (Recommended)

The only real fix is to configure the server to send the correct headers.

Express (Node.js):

bash
npm install cors
javascript
import cors from 'cors';

app.use(cors({
  origin: 'http://localhost:3000', // your frontend origin
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true, // only if you send cookies or auth headers
}));

For development only — allow all origins:

javascript
app.use(cors()); // allows *

Django (Python):

bash
pip install django-cors-headers
python
# settings.py
INSTALLED_APPS = ['corsheaders', ...]
MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware', ...]

CORS_ALLOWED_ORIGINS = ['http://localhost:3000']
CORS_ALLOW_CREDENTIALS = True

Nginx:

nginx
location /api/ {
  add_header 'Access-Control-Allow-Origin' 'https://yourfrontend.com' always;
  add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
  add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
  add_header 'Access-Control-Allow-Credentials' 'true' always;

  if ($request_method = OPTIONS) {
    return 204;
  }
}

Fix During Development — Proxy

In local development, configure your dev server to proxy API requests so they appear to come from the same origin.

Next.js (next.config.js):

javascript
module.exports = {
  async rewrites() {
    return [{ source: '/api/:path*', destination: 'http://localhost:8000/api/:path*' }];
  },
};

Vite (vite.config.js):

javascript
export default {
  server: {
    proxy: {
      '/api': { target: 'http://localhost:8000', changeOrigin: true },
    },
  },
};

Create React App (package.json):

json
{ "proxy": "http://localhost:8000" }

Debugging CORS Errors

1. Open the browser Network tab (F12) 2. Find the blocked request 3. Check if there is a preflight OPTIONS request before it — did it succeed? 4. Check the Response Headers of the actual or OPTIONS request — is `Access-Control-Allow-Origin` present? 5. Check the console error message — it usually tells you exactly which header is missing

What NOT to Do

Do not install browser extensions to disable CORS in production — it only bypasses it in your browser and does not fix the real problem. Do not add the API response as `no-cors` mode in fetch — this makes the response opaque and you cannot read any data from it.

Try JSON Validator

Validate the JSON response body once you've resolved the CORS issue.