If you’ve ever built a web app that talks to a backend API, you’ve probably run into this scary message:
❌ “Access to fetch at ‘some-api.com’ from origin ‘your-site.com’
has been blocked by CORS policy...”
Sounds confusing? Don’t worry — this article will explain what CORS is, why it exists, and how to fix it.
The Real Problem: Different Origins
Let’s say:
- Your frontend is hosted at: https://mycoolapp.com
- Your backend API lives at: https://api.mycoolapp.com
Even though they look similar, the browser sees them as different places, called origins.
An origin is made up of:
Origin = protocol + domain + port
If any of these are different, the browser treats it as a cross-origin request
And that’s when CORS steps in.
So What is CORS?
CORS stands for Cross-Origin Resource Sharing.
It’s a security rule built into all modern browsers that says:
“I’ll only let your website fetch data from another origin if that origin allows it.”
That means — even if your API is working fine, the browser can still block your request unless the API sends back the right permissions.
Why Does the Browser Do This?
It’s there to protect users.
Imagine:
- You’re logged into your bank.
- You visit a shady site.
- That site secretly tries to make a request to your bank using your login session.
Without CORS, the shady site could potentially succeed.
So browsers add a safety check:
“You can’t send requests to other websites unless they clearly say it’s okay”
When Do CORS Errors Happen?
CORS errors usually happen when your website tries to talk to another origin. For example:
✅ Your site: https://example.com
❌ Tries to call:
- http://example.com (different protocol)
- https://api.example.com (different subdomain)
- https://backend.com (different domain)
- https://example.com:8080 (different port)
Other reasons CORS might fail:
- The backend doesn’t return the correct CORS headers
- You’re sending Authorization headers or cookies
- You’re using PUT, DELETE, or custom headers — which triggers a preflight request
If the browser doesn’t get permission, it blocks the request.
What’s a Preflight Request?
A preflight request is like asking for permission before doing something sensitive.
Let’s say your frontend wants to:
- Use the PUT method
- Send application/json data
- Include a token in headers The browser says:
“Hold on! Let me check with the server first.”
So it sends an hidden OPTIONS request - this is called “preflight.”
If the server replies properly, your request goes through ✅
If not, the browser blocks it 💥
How the OPTIONS Request Works
Browser sends:
OPTIONS /api/user HTTP/1.1
Origin: https://mycoolapp.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
Server must respond:
Access-Control-Allow-Origin: https://mycoolapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
If the server doesn’t send these headers, the request will be blocked.
How to Fix CORS
If you control the backend, here’s what you need to do:
- Allow your frontend’s origin
- Handle the OPTIONS method
- Allow necessary headers and methods
- If you’re using cookies or auth tokens, set Access-Control-Allow-Credentials: true
Example: Fixing CORS in Node.js (Express)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://mycoolapp.com', // allow your frontend
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: true, // allow cookies/tokens
allowedHeaders: ['Content-Type', 'Authorization']
}));
app.options('*', cors()); // Handle preflight
Quick Troubleshooting Checklist
✅ CORS Debugging Checklist
✅ Check | Example
------------------------------------------|---------------------------------------------------
Is the origin allowed? | Access-Control-Allow-Origin: https://mycoolapp.com
Are custom headers allowed? | Authorization, X-Custom-Header
Is the preflight request handled? | Allow OPTIONS requests
Are credentials (cookies/tokens) enabled? | Access-Control-Allow-Credentials: true
Are you using * with credentials? | ❌ Not allowed. Use exact origin instead