How to Troubleshoot CORS Errors When Calling a Web API (September 2026) Guide

Every web developer has been there. You write a clean fetch call, everything looks perfect, and then the browser console lights up red with a message about Access-Control-Allow-Origin. Learning how to troubleshoot CORS errors when calling a web API is one of those skills that saves hours of frustration and keeps your projects on track.

In this guide, I will walk you through what CORS actually is, why browsers block your requests, and exactly how to diagnose and fix these errors. By the end, you will have a repeatable debugging workflow, code examples for every major backend framework, and the confidence to tackle any CORS issue that comes your way.

What Is CORS and Why Does It Block Your API Calls?

CORS stands for Cross-Origin Resource Sharing. It is a browser security mechanism that controls how web pages can request resources from a domain different from the one that served the page. When your frontend at https://myapp.com tries to call an API at https://api.example.com, the browser steps in and checks whether the API has explicitly permitted requests from your origin.

This security feature exists because of something called the same-origin policy. The same-origin policy is a foundational browser rule that prevents a website from reading data returned by a different website without permission. Without it, any site you visit could silently read your authenticated sessions on banking sites, email providers, or any other logged-in service.

CORS is the controlled relaxation of that policy. It gives servers a way to say, “Yes, I allow this specific origin to access my resources.” The browser does the enforcement. Your server does the configuration.

The security rationale matters here. CORS protects users from cross-site request forgery (CSRF) attacks, where a malicious page makes unauthorized requests on behalf of an authenticated user. When you understand that CORS exists to protect your users, not to annoy developers, the troubleshooting process starts making more sense.

How CORS Actually Works: The Request Flow

Not every cross-origin request triggers the same CORS behavior. The browser distinguishes between two types of requests: simple requests and preflight requests.

A simple request is one the browser sends directly without checking first. To qualify, the request must use a safe method (GET, HEAD, or POST), use only CORS-safelisted headers (Accept, Accept-Language, Content-Language, Content-Type with restrictions), and set Content-Type to text/plain, multipart/form-data, or application/x-www-form-urlencoded. If your request meets all these conditions, the browser sends it immediately and then checks the response headers for CORS permission.

A preflight request happens when your API call does not qualify as simple. Before sending the actual request, the browser sends an OPTIONS request to the server. This preflight asks, “Are you okay with this method, these headers, and this origin?” The server must respond with appropriate CORS headers. Only then does the browser send the real request.

Most modern API calls using application/json trigger a preflight because application/json is not a safelisted Content-Type. Understanding whether your request is simple or preflight changes how you debug it.

Here are the key CORS headers the browser looks for in responses:

Access-Control-Allow-Origin — Specifies which origins may access the resource. Set to a specific origin like https://myapp.com or * for any origin.

Access-Control-Allow-Methods — Lists the HTTP methods permitted (GET, POST, PUT, DELETE, etc.).

Access-Control-Allow-Headers — Lists which request headers are allowed in the actual request.

Access-Control-Allow-Credentials — When set to true, allows cookies and HTTP authentication to accompany the request.

Access-Control-Max-Age — Tells the browser how long (in seconds) to cache the preflight response, reducing repeated OPTIONS calls.

Access-Control-Expose-Headers — Lets the frontend JavaScript read specific response headers beyond the safelisted defaults.

How to Identify CORS Errors in the Browser Console

The first place CORS errors show up is your browser’s developer tools console. The error messages are specific and tell you exactly what went wrong, but only if you know how to read them.

Here is what a typical CORS error looks like in Chrome:

Access to fetch at 'https://api.example.com/data' from origin 'https://myapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

That message tells you the server did not include the Access-Control-Allow-Origin header in its response. The browser blocked your JavaScript from reading the response as a result.

Here are the most common CORS error messages and what each one means:

“No ‘Access-Control-Allow-Origin’ header is present” — The server is not sending CORS headers at all. You need to configure CORS on the server side.

“The value of the ‘Access-Control-Allow-Origin’ header must not be the wildcard ‘*’ when the request’s credentials mode is ‘include'” — You are sending credentials (cookies or auth headers) but the server responded with * instead of a specific origin. These two settings are incompatible.

“Method PUT is not allowed by Access-Control-Allow-Methods in preflight response” — The server allows some methods but not the one your app is using. Add the missing method to the server’s CORS configuration.

“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response” — Your custom headers are not whitelisted on the server. Add them to Access-Control-Allow-Headers.

“Redirect is not allowed for a preflight request” — The server is redirecting the preflight OPTIONS request. CORS requires preflight responses to not redirect.

To diagnose CORS errors properly in Chrome DevTools, follow this workflow:

Step 1: Open DevTools with F12 (or right-click and Inspect) and go to the Console tab. Look for the CORS error message highlighted in red.

Step 2: Switch to the Network tab. Reload the page and find the failed request. If you see an OPTIONS request before your actual request, that is the preflight. Click it to inspect its response headers.

Step 3: In the Headers tab of the failed request, scroll to the Response Headers section. Look for any Access-Control-* headers. Their absence or incorrect values reveal the problem.

Step 4: Check the Status Code. A 200 response with missing CORS headers is a configuration issue. A 4xx or 5xx status code means the request itself failed, and CORS is a secondary symptom.

How to Troubleshoot CORS Errors: Step-by-Step Debugging Workflow

When a CORS error appears, panic is the wrong response. A systematic debugging workflow is the right one. I have refined this process over years of building web applications, and it works for almost every CORS scenario.

Step 1: Confirm the error is actually CORS. Read the console message carefully. If the error mentions network failure, SSL certificates, or DNS, CORS is not your problem. CORS errors always mention “CORS policy” or “Access-Control-Allow-Origin” explicitly.

Step 2: Reproduce the request outside the browser. Copy the request URL and test it with curl or Postman. Browsers do not enforce CORS on these tools, so a successful request here confirms the API works and the issue is CORS-specific.

Here is how to test with curl and see the response headers:

curl -I -X OPTIONS https://api.example.com/data -H "Origin: https://myapp.com" -H "Access-Control-Request-Method: GET"

The -I flag shows response headers only. Look for Access-Control-Allow-Origin in the output. If it is missing, the server needs CORS configuration.

Step 3: Determine if the issue is server-side or client-side. If you control the API server, the fix belongs on the server. If you are calling a third-party API, you need a proxy solution (covered below).

Step 4: Check whether the request triggers a preflight. Open the Network tab and look for an OPTIONS request before your actual API call. If it exists and fails, your server needs to handle OPTIONS requests properly.

Step 5: Inspect the preflight response headers. The OPTIONS response must include Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers. Missing any of these causes the preflight to fail.

Step 6: Verify credentials handling. If your request includes credentials: 'include' in fetch or withCredentials: true in axios, the server must respond with Access-Control-Allow-Credentials: true and a specific origin (never *).

Step 7: Check for redirect chains. If the API redirects from HTTP to HTTPS or from one domain to another, CORS headers may be stripped during the redirect. CORS does not allow redirects on preflight requests at all.

Step 8: Test in a different browser. Firefox and Chrome occasionally handle edge cases differently. If the error appears in one browser but not another, you may have hit a browser-specific quirk or extension interference.

Step 9: Compare development and production behavior. If CORS works in dev but fails in production, check whether your production origin matches what the server allows. Environment variables for allowed origins are a common culprit.

Step 10: Review server logs. Check if the OPTIONS request even reached your server. Some load balancers and reverse proxies intercept OPTIONS requests before they reach your application code.

Server-Side CORS Configuration Fixes

The most reliable way to fix CORS errors is to configure the API server correctly. The server needs to send the right headers on every response, including preflight OPTIONS responses. Here are code examples for the most common backend frameworks.

Node.js with Express

The cors package is the standard solution for Express applications. Install it with npm install cors and configure it as middleware:

const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({ origin: 'https://myapp.com', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true }));

If you need to allow multiple origins, pass a function that checks the request origin against an allowlist:

const allowedOrigins = ['https://myapp.com', 'https://staging.myapp.com'];
app.use(cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: true }));

Avoid using origin: '*' with credentials: true. Browsers reject this combination, and your request will fail.

Python with Flask

For Flask, use the flask-cors extension. Install it with pip install flask-cors:

from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins=['https://myapp.com'], supports_credentials=True)

For per-route CORS configuration, apply the decorator directly:

@app.route('/api/data')
@cross_origin(origins='https://myapp.com', methods=['GET', 'POST'])
def get_data(): return {'status': 'ok'}

ASP.NET Core

In ASP.NET Core, register CORS in Program.cs or Startup.cs:

builder.Services.AddCors(options => { options.AddPolicy('MyPolicy', policy => { policy.WithOrigins('https://myapp.com').AllowAnyMethod().AllowAnyHeader().AllowCredentials(); }); });
var app = builder.Build();
app.UseCors('MyPolicy');

Make sure app.UseCors() is called before app.UseAuthorization() and app.MapControllers(). Order matters in the middleware pipeline.

Nginx Configuration

If you use Nginx as a reverse proxy, add CORS headers in the server or location block:

add_header 'Access-Control-Allow-Origin' 'https://myapp.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
if ($request_method = 'OPTIONS') { return 204; }

The 204 response for OPTIONS ensures the preflight returns quickly without a body.

Amazon API Gateway

For API Gateway, enable CORS from the console or via configuration. Go to your API, select the resource, click “Enable CORS,” and configure the allowed origins, headers, and methods. If you use Lambda integration, make sure your Lambda function also returns CORS headers in its response. API Gateway does not automatically merge its CORS settings with Lambda response headers.

A common issue with API Gateway and Lambda: the preflight OPTIONS request succeeds but the actual request fails. This happens when the OPTIONS mock integration is configured but the real Lambda does not include Access-Control-Allow-Origin in its response body headers.

Frontend-Side Fixes and Development Workarounds

Sometimes you cannot change the server. Other times, you need a local development solution that avoids CORS entirely. These frontend and development-time fixes keep you productive without compromising security in production.

Vite Development Proxy

If you use Vite (common with React, Vue, and Svelte), configure a proxy in vite.config.js. The dev server forwards API requests to your backend, making them same-origin from the browser’s perspective:

export default defineConfig({ server: { proxy: { '/api': { target: 'https://api.example.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } });

Now your frontend calls /api/data and Vite proxies it to https://api.example.com/data. No CORS error because the browser sees a same-origin request.

Next.js API Rewrites

Next.js provides a similar proxy mechanism through rewrites in next.config.js:

module.exports = { async rewrites() { return [ { source: '/api/:path*', destination: 'https://api.example.com/:path*' } ]; } };

This works for both development and production when hosted on Vercel, since Vercel handles the proxy at the edge.

The no-cors Mode (Know Its Limits)

The fetch API supports a mode: 'no-cors' option. Many developers try this first, but it rarely solves the real problem. With no-cors, the browser sends the request but returns an opaque response. You cannot read the response body, check the status code, or access any headers from JavaScript.

Use no-cors only for fire-and-forget scenarios like sending analytics pings or service worker cache operations. For API calls where you need the response data, no-cors will not help.

Browser Extensions for Local Development

Several Chrome extensions disable CORS for development. Extensions like “Allow CORS: Access-Control-Allow-Origin” modify response headers on the fly. These are useful for quick testing but should never be relied on for actual development workflows or left enabled in production browsing.

I recommend using dev server proxies instead of extensions. Proxies are more reliable, work across your entire team, and do not require everyone to install the same extension.

Localhost Specifics

Localhost gets special treatment in some browsers. Chrome treats http://localhost:3000 as a potentially trustworthy origin, but it is still a different origin from http://localhost:8080. If your frontend runs on port 3000 and your API on port 5000, that is a cross-origin request and CORS applies.

Include http://localhost:3000 (or whatever port you use) in your server’s allowed origins during development. Remove it or gate it behind an environment check for production builds.

Proxy Server Solutions When You Don’t Control the API

When you call a third-party API that does not support CORS, you cannot fix it on the server side. You need an intermediary that adds CORS headers to the response. This is where proxy servers come in.

cors-anywhere

The cors-anywhere project is a Node.js proxy that adds CORS headers to any response. You can use the public demo instance for testing, but do not rely on it for production. Rate limits and downtime make it unreliable.

For real projects, self-host cors-anywhere on your own server or deploy it to a platform like Heroku, Railway, or Render:

// Install and run locally
git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere && npm install
PORT=8080 node server.js

Then prepend the proxy URL to your API calls: http://localhost:8080/https://api.example.com/data

Serverless Function Proxy

A serverless function is a cleaner approach for production. Deploy a simple function on AWS Lambda, Vercel, Netlify, or Cloudflare Workers that fetches the third-party API and returns the response with CORS headers:

export default async function handler(req, res) { const response = await fetch('https://api.example.com/data'); const data = await response.json(); res.setHeader('Access-Control-Allow-Origin', 'https://myapp.com'); res.status(200).json(data); }

This approach gives you full control over headers, rate limiting, caching, and error handling. It also keeps your API keys server-side, which is more secure than exposing them in frontend code.

Build Your Own Proxy

If you already have a backend, add a proxy endpoint. Your frontend calls your own API, and your server forwards the request to the third-party service. Server-to-server requests do not enforce CORS because CORS is a browser-only mechanism.

This is the most production-ready solution. You control caching, authentication, error handling, and monitoring all in one place.

Common CORS Mistakes and How to Avoid Them

After debugging hundreds of CORS issues across projects, I see the same mistakes repeatedly. Here are the top pitfalls and how to avoid them.

Mistake 1: Using wildcard origin with credentials. The combination of Access-Control-Allow-Origin: * and credentials: 'include' always fails. Browsers reject this for security reasons. Always use a specific origin when credentials are involved.

Mistake 2: Forgetting to handle OPTIONS requests. Your server middleware may skip OPTIONS requests or return a 404. The preflight must return a 200 or 204 with the correct CORS headers. In Express, place the cors() middleware before route handlers. In other frameworks, ensure OPTIONS is explicitly handled.

Mistake 3: Redirects that strip CORS headers. Redirecting from HTTP to HTTPS or from api.example.com to www.api.example.com can strip CORS headers from the response. The browser then blocks the request. Fix this by ensuring the final destination of any redirect includes proper CORS headers, and avoid redirects on preflight requests entirely.

Mistake 4: Setting CORS headers on the client. You cannot set Access-Control-Allow-Origin on your fetch request. That header is a response header, not a request header. Adding it to your request does nothing except potentially trigger another CORS error.

Mistake 5: Environment-specific origins in production. Your dev environment allows http://localhost:3000. Production needs https://myapp.com. If you hardcode the localhost origin, production requests fail. Use environment variables for allowed origins and verify them during deployment.

Mistake 6: OAuth flows breaking CORS. OAuth redirects involve multiple origins and redirects, which complicate CORS. Authorization servers may not include CORS headers on redirect responses. Use server-side OAuth flows or configure your authorization server to return proper CORS headers for all relevant origins.

Mistake 7: Assuming CORS errors mean the server is broken. CORS errors often mask successful server responses. The API may have returned the correct data with a 200 status, but because CORS headers were missing, the browser blocked your JavaScript from reading it. Always check server logs to confirm the request actually reached and was processed by your application.

Mistake 8: Relying on browser extensions in production. Extensions that disable CORS only work on the machine where they are installed. If your app requires users to install an extension, something is fundamentally wrong with your architecture.

FAQs

What is a CORS error?

A CORS error occurs when a browser blocks a cross-origin request because the server did not include the proper Access-Control-Allow-Origin header in its response. The browser enforces the same-origin policy and prevents JavaScript from reading the response unless the server explicitly grants permission to the requesting origin.

How to fix CORS error in JavaScript fetch?

To fix a CORS error in JavaScript fetch, configure the server to include Access-Control-Allow-Origin in its response headers matching your origin. If you do not control the server, use a proxy server or a dev server proxy in Vite or Next.js. You cannot fix CORS purely from the frontend fetch call itself.

Why do I get a CORS error when calling an API from localhost?

Localhost on one port is a different origin from localhost on another port. If your frontend runs on localhost:3000 and your API on localhost:5000, the browser treats this as a cross-origin request. Add your localhost origin to the server’s allowed origins list or use a dev server proxy.

How to fix CORS error in React?

To fix CORS errors in React, configure your backend server with the correct CORS headers, or set up a proxy in your Vite or Create React App development server. For production, route API calls through your own backend or a serverless function proxy.

How to fix CORS error in Chrome?

Chrome enforces CORS strictly. To fix CORS errors in Chrome, configure the server to return proper CORS headers. For local development, use a dev proxy or a browser extension that disables CORS for testing. Never rely on extensions for production applications.

Can I fix CORS errors without server access?

Yes, but only through a proxy. Set up a serverless function, use a self-hosted proxy like cors-anywhere, or route requests through your own backend server. The proxy adds the necessary CORS headers to the response before returning it to the browser.

What is a CORS preflight request?

A CORS preflight request is an OPTIONS request the browser sends before the actual API call to check if the server allows the request method, headers, and origin. It is triggered by requests using methods other than GET, HEAD, or POST with simple content types, or when custom headers are included.

Conclusion

CORS errors frustrate every developer at some point, but they follow predictable patterns once you understand the mechanism. The browser enforces same-origin policy, your server controls the response headers, and the fix always involves either proper server configuration or a proxy intermediary.

Now you know how to troubleshoot CORS errors when calling a web API using a repeatable step-by-step workflow. Start by confirming the error is truly CORS, test outside the browser with curl, inspect the Network tab for missing headers, and apply the server-side or proxy fix that fits your situation. Keep this guide bookmarked, and the next red console message will be just another puzzle to solve.

Leave a Comment