Web Development

CORS Error: Complete Guide to Fixing Cross-Origin Resource Sharing Issues

March 25, 2024
11 min read
By Saad Minhas
CORSWeb DevelopmentAPIBackendTroubleshootingReact
CORS Error: Complete Guide to Fixing Cross-Origin Resource Sharing Issues

CORS (Cross-Origin Resource Sharing) errors are one of the most frustrating issues developers face when building web applications. If you've seen the error "Access to fetch at '...' from origin '...' has been blocked by CORS policy," you know what I mean.


What is CORS?


CORS is a security mechanism implemented by browsers that restricts web pages from making requests to a different domain, protocol, or port than the one serving the web page.


Example:


  • Your frontend is at: `https://myapp.com`
  • Your API is at: `https://api.myapp.com`
  • Browser blocks the request because they're different origins

Why CORS Errors Happen


CORS errors occur when:

1. Different domains: Frontend and backend are on different domains

2. Different ports: Frontend on port 3000, backend on port 5000

3. Different protocols: HTTP vs HTTPS

4. Missing CORS headers: Backend doesn't send proper CORS headers


Common CORS Error Messages


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.

How to Fix CORS Errors


Solution 1: Enable CORS on Your Backend (Recommended)


For Express.js/Node.js:


// Install cors package
npm install cors

// Basic setup
const express = require('express');
const cors = require('cors');
const app = express();

// Enable CORS for all routes
app.use(cors());

app.get('/api/data', (req, res) => {
  res.json({ message: 'Hello from API' });
});

app.listen(5000);

Configure CORS with options:


const cors = require('cors');

const corsOptions = {
  origin: 'https://myapp.com', // Allow only your frontend
  // Or allow multiple origins
  // origin: ['https://myapp.com', 'https://www.myapp.com'],
  
  methods: ['GET', 'POST', 'PUT', 'DELETE'], // Allowed methods
  allowedHeaders: ['Content-Type', 'Authorization'], // Allowed headers
  credentials: true, // Allow cookies/credentials
};

app.use(cors(corsOptions));

For development (allow all origins):


const corsOptions = {
  origin: process.env.NODE_ENV === 'production' 
    ? 'https://myapp.com' 
    : true, // Allow all origins in development
  credentials: true,
};

app.use(cors(corsOptions));

Solution 2: Manual CORS Headers (Without Package)


app.use((req, res, next) => {
  // Allow requests from your frontend
  res.header('Access-Control-Allow-Origin', 'https://myapp.com');
  
  // Allow credentials (cookies, authorization headers)
  res.header('Access-Control-Allow-Credentials', 'true');
  
  // Allow specific methods
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  
  // Allow specific headers
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  // Handle preflight requests
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  
  next();
});

Solution 3: Next.js API Routes (No CORS Needed)


If you're using Next.js, use API routes as a proxy:


// pages/api/proxy.ts
export default async function handler(req, res) {
  // Next.js API routes are same-origin, so no CORS needed
  const response = await fetch('https://external-api.com/data', {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`
    }
  });
  
  const data = await response.json();
  res.json(data);
}

// In your frontend
const response = await fetch('/api/proxy'); // Same origin, no CORS!

Solution 4: Fix CORS in React/Next.js Frontend


Using Next.js API Routes (Best Practice):


// pages/api/users.ts
export default async function handler(req, res) {
  // This runs server-side, so no CORS issues
  const response = await fetch('https://external-api.com/users', {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`
    }
  });
  
  const data = await response.json();
  res.json(data);
}

// In your component
const fetchUsers = async () => {
  const response = await fetch('/api/users'); // Same origin!
  const users = await response.json();
  return users;
};

If you must call external API directly:


// Only works if the API has CORS enabled
const fetchData = async () => {
  try {
    const response = await fetch('https://api.example.com/data', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
      credentials: 'include', // Include cookies if needed
    });
    
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('CORS or fetch error:', error);
  }
};

Solution 5: Development Proxy (Create React App)


If using Create React App, add proxy to package.json:


{
  "name": "my-app",
  "proxy": "http://localhost:5000"
}

Then in your code:


// Instead of http://localhost:5000/api/data
fetch('/api/data') // Proxy handles it

Solution 6: Handle Preflight Requests


Browsers send OPTIONS requests (preflight) before certain requests:


// Handle preflight requests
app.options('*', cors()); // Handle all OPTIONS requests

// Or handle specific route
app.options('/api/data', cors());

Common CORS Scenarios


Scenario 1: Frontend and Backend on Different Ports


Problem:

  • Frontend: `http://localhost:3000`
  • Backend: `http://localhost:5000`

Solution:


// Backend (Express)
const cors = require('cors');
app.use(cors({
  origin: 'http://localhost:3000',
  credentials: true
}));

Scenario 2: Production Deployment


Problem:

  • Frontend: `https://myapp.com`
  • Backend: `https://api.myapp.com`

Solution:


const corsOptions = {
  origin: [
    'https://myapp.com',
    'https://www.myapp.com'
  ],
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

Scenario 3: Third-Party API Without CORS


Problem:

External API doesn't allow CORS, and you can't modify it.


Solution:

Use a backend proxy (Next.js API route or Express server):


// Backend proxy
app.get('/api/proxy', async (req, res) => {
  const response = await fetch('https://external-api.com/data', {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`
    }
  });
  const data = await response.json();
  res.json(data);
});

// Frontend calls your proxy
fetch('/api/proxy'); // No CORS issue!

Testing CORS


Test with curl:


# Check if CORS headers are present
curl -H "Origin: https://myapp.com"      -H "Access-Control-Request-Method: GET"      -H "Access-Control-Request-Headers: Content-Type"      -X OPTIONS      https://api.example.com/data      -v

Check response headers:

  • `Access-Control-Allow-Origin`
  • `Access-Control-Allow-Methods`
  • `Access-Control-Allow-Headers`

Security Best Practices


1. Don't use origin: '*' in production

  • - Only allow specific origins
  • - Use environment variables

2. Limit allowed methods and headers

  • - Only allow what you need

3. Use credentials carefully

  • - Only enable if you need cookies/auth headers

4. Validate origin on server

  • - Don't trust client-side validation

const allowedOrigins = [
  'https://myapp.com',
  'https://www.myapp.com'
];

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
};

Common Mistakes


1. Forgetting to handle OPTIONS requests

2. Using origin: '*' with credentials

3. Not setting credentials: true when needed

4. Mismatched origins (http vs https)

5. Not testing in production environment


Conclusion


CORS errors are common but fixable. The key points:


1. Enable CORS on your backend with proper configuration

2. Use Next.js API routes as a proxy when possible

3. Never use origin: '*' in production

4. Handle preflight (OPTIONS) requests

5. Test CORS in your deployment environment


Remember: CORS is a browser security feature. It can't be bypassed from the frontend—you must configure it on the backend.

Get In Touch

Connect

Full Stack Software Engineer passionate about building innovative web and mobile applications.

CEO at Appzivo— a product and engineering studio for larger engagements.

© 2026 Saad Minhas. All Rights Reserved.