The Error Everyone Hits
You're building a React app. API works in Postman. Works in curl. Works everywhere.
Then you put it in your React component:
useEffect(() => {
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.log(err))
}, [])
Browser console:
Access to XMLHttpRequest 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.
Why? Browser security. Not your fault. But also... frustrating.
Here's what actually works:
Solution 1: Backend Fix (The Right Way)
The correct fix is on the backend, not frontend.
Your backend needs to send:
Access-Control-Allow-Origin: http://localhost:3000
If you control the backend:
Node.js/Express:
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:3000', // Your frontend URL
credentials: true
}));
app.get('/api/data', (req, res) => {
res.json({ message: 'success' });
});
Laravel/PHP:
header('Access-Control-Allow-Origin: http://localhost:3000');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Credentials: true');
header('Content-Type: application/json');
Python/Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins=['http://localhost:3000'])
@app.route('/api/data')
def data():
return {'message': 'success'}
That's it. Your React code now works.
Solution 2: Proxy During Development
If you DON'T control the backend, or you're developing locally:
Create a proxy in package.json:
{
"proxy": "https://api.example.com"
}
Then in React, remove the domain:
// ❌ Before (gets CORS error)
fetch('https://api.example.com/data')
// ✅ After (goes through proxy)
fetch('/data')
Why this works: React's dev server acts as a middleman. Browsers don't block same-origin requests.
⚠️ Important: This only works in development (npm start). Production builds need the backend fix.
Solution 3: CORS Proxy (Temporary, Not Recommended)
If you're stuck and can't change the backend:
const corsProxy = 'https://cors-anywhere.herokuapp.com/';
const apiUrl = 'https://api.example.com/data';
fetch(corsProxy + apiUrl)
.then(res => res.json())
.then(data => console.log(data))
Why this sucks:
- Extra latency (request goes through a third server)
- Free CORS proxies get rate-limited
- Security risk (your data goes through someone else's server)
- Will break in production
Use this only for testing. Don't ship it.
Solution 4: Credentials & Cookies
If your API requires authentication (cookies, JWT):
// ❌ Doesn't send cookies
fetch('https://api.example.com/data')
// ✅ Sends cookies
fetch('https://api.example.com/data', {
credentials: 'include'
})
Backend also needs to allow credentials:
Express:
cors({
origin: 'http://localhost:3000',
credentials: true // ← This line
})
The Checklist (Before Asking for Help)
-
✅ Does your backend have CORS headers set?
- Check:
curl -I https://api.example.com/data | grep Access-Control - If nothing appears → backend needs fixing
- Check:
-
✅ Is it a preflight request being blocked?
- Look for
OPTIONSrequest in Network tab - If it fails → backend doesn't handle preflight
- Look for
-
✅ Are you sending credentials?
- If yes → both frontend needs
credentials: 'include'AND backend needscredentials: true
- If yes → both frontend needs
-
✅ Is this production or development?
- Dev: use proxy
- Production: backend must have CORS headers
Real Example: Fetch with All Options
Here's a complete, production-ready example:
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
credentials: 'include' // Include cookies if backend requires auth
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('CORS or network error:', error);
}
}
What I've Learned (After Fixing This 100+ Times)
- 99% of CORS errors are a backend configuration issue, not a React problem
- The proxy solution feels like magic locally, but it's a trap for production — always fix the backend
- Credentials require matching configurations on both sides — one side correct isn't enough
- Preflight requests (OPTIONS) are the actual blocker — if those fail, everything fails
The Fastest Debug Path
- Open DevTools → Network tab
- Try your API call
- Look for the failed request
- Click it → Response headers → search for
Access-Control-Allow-Origin - If it's missing → backend problem
- If it's there but says a different origin → update backend to allow your URL
Still Stuck?
Drop the actual error message in the comments. Knowing:
- What frontend (React, Vue, etc.) you're using
- What backend (Express, Laravel, etc.) you're using
- Whether this is local dev or production
...helps me (and others) give you the exact fix.
Cheers ☕
Want more React debugging tips? I write practical solutions to the problems that actually kill deployments. Check out my full blog for deep dives on error handling, performance, and production gotchas.
Author: Ankit Khoiwal | Full-stack developer | Every post is from real production experience
Top comments (0)