CORS Configuration Problems
What This Means
Cross-Origin Resource Sharing (CORS) is a security mechanism that controls how web pages from one domain can access resources from another domain. CORS errors occur when browsers block cross-origin requests due to missing or incorrect CORS headers, preventing your frontend from communicating with APIs, loading fonts, or integrating third-party services.
Common CORS Problems
Missing CORS Headers:
- No
Access-Control-Allow-Originheader - Headers not sent from API/server
- Wrong header format or values
- Headers missing for preflight requests
Configuration Errors:
- Wildcard
*with credentials - Wrong origin specified
- Missing allowed methods/headers
- Preflight requests failing
Common Error Messages:
Access to fetch at 'https://api.example.com' from origin 'https://example.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is
present on the requested resource.
Access to XMLHttpRequest has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
Impact on Your Business
Functionality Breaks:
- API calls fail completely
- Forms don't submit
- Data doesn't load
- Third-party integrations broken
- Fonts don't load (slow fallback fonts)
User Experience:
- Features appear broken
- Error messages shown to users
- Page functionality missing
- Slow page loads (font fallback)
Business Impact:
- Lost conversions from broken forms
- Customer frustration
- Support tickets increase
- Revenue loss from broken features
- Abandoned integrations
Development:
- Delayed deployments
- Complex workarounds needed
- Testing difficulties
- Security vulnerabilities if misconfigured
How to Diagnose
Method 1: Browser Console (Recommended)
- Open DevTools (
F12) - Navigate to "Console" tab
- Trigger the failing request
- Look for CORS error messages
What to Look For:
Access to fetch at 'https://api.example.com/data' from origin
'https://mysite.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on
the requested resource.
Method 2: Network Tab Analysis
- Open DevTools → "Network" tab
- Make the failing request
- Click on failed request
- Check "Headers" tab
What to Look For:
- Status Code: 200 but CORS error
- Missing
Access-Control-Allow-Originin Response Headers - OPTIONS request (preflight) failing
Response Headers should include:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Method 3: cURL Testing
Test from command line:
# Simple request
curl -H "Origin: https://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
-X OPTIONS \
--verbose \
https://api.example.com/endpoint
# Look for headers in response
What to Look For:
- HTTP 200 OK for OPTIONS
- CORS headers in response
- Correct origin in Allow-Origin
Method 4: Online CORS Testers
- Visit test-cors.org
- Enter your API endpoint
- Test different origins
- Review results
What to Look For:
- Which origins are allowed
- Which methods are allowed
- Whether credentials are supported
Method 5: Check Preflight Requests
Preflight requests happen for:
- POST/PUT/PATCH/DELETE methods
- Custom headers (Authorization, etc.)
- Content-Type other than form data
In Network tab:
- Look for OPTIONS request before actual request
- OPTIONS must return 200
- Must include CORS headers
General Fixes
Fix 1: Configure Server CORS Headers
Express.js (Node.js):
const express = require('express');
const cors = require('cors');
const app = express();
// Simple usage - allow all origins (development only)
app.use(cors());
// Production - specific origins
app.use(cors({
origin: 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // 24 hours
}));
// Multiple origins
const allowedOrigins = [
'https://example.com',
'https://www.example.com',
'https://staging.example.com'
];
app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
Manual headers (Express.js):
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', 'true');
// Handle preflight
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
Fix 2: Configure Apache CORS
Apache (.htaccess):
<IfModule mod_headers.c>
# Allow from specific origin
Header set Access-Control-Allow-Origin "https://example.com"
# Allow credentials
Header set Access-Control-Allow-Credentials "true"
# Allowed methods
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
# Allowed headers
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
# Preflight cache
Header set Access-Control-Max-Age "86400"
</IfModule>
# Handle preflight OPTIONS requests
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>
Multiple origins:
<IfModule mod_headers.c>
SetEnvIf Origin "^https://(www\.)?example\.com$" ORIGIN=$0
SetEnvIf Origin "^https://staging\.example\.com$" ORIGIN=$0
Header always set Access-Control-Allow-Origin "%{ORIGIN}e" env=ORIGIN
Header always set Access-Control-Allow-Credentials "true" env=ORIGIN
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" env=ORIGIN
Header always set Access-Control-Allow-Headers "Content-Type, Authorization" env=ORIGIN
</IfModule>
Fix 3: Configure Nginx CORS
Nginx configuration:
# Add CORS headers
location /api/ {
# Simple requests
add_header 'Access-Control-Allow-Origin' 'https://example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
# Preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass http://backend;
}
Multiple origins:
map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://staging\.example\.com$" $http_origin;
}
location /api/ {
if ($cors_origin != "") {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
# ... rest of config
}
Fix 4: Handle Credentials Properly
Cannot use wildcard with credentials:
// Wrong - doesn't work
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
// Correct - specific origin
res.header('Access-Control-Allow-Origin', 'https://example.com');
res.header('Access-Control-Allow-Credentials', 'true');
// Or dynamically match origin
const allowedOrigins = ['https://example.com', 'https://staging.example.com'];
if (allowedOrigins.includes(req.headers.origin)) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Credentials', 'true');
}
Client-side (fetch):
// Include credentials
fetch('https://api.example.com/data', {
method: 'POST',
credentials: 'include', // Important!
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
Fix 5: Fix Preflight Request Issues
Ensure OPTIONS requests succeed:
// Express.js
app.options('*', cors()); // Enable preflight for all routes
// Or manually handle OPTIONS
app.use((req, res, next) => {
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Origin', 'https://example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Max-Age', '86400');
return res.sendStatus(200);
}
next();
});
Fix 6: Use Proxy for Development
Development proxy to avoid CORS:
{
"proxy": "http://localhost:3001"
}
Vue (vue.config.js):
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true
}
}
}
};
Webpack:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3001',
pathRewrite: {'^/api': ''},
changeOrigin: true
}
}
}
};
Fix 7: Use CORS Proxy (Last Resort)
Only for development/testing:
// Using cors-anywhere or similar
const API_URL = process.env.NODE_ENV === 'production'
? 'https://api.example.com'
: 'https://cors-anywhere.herokuapp.com/https://api.example.com';
fetch(`${API_URL}/data`)
.then(res => res.json())
.then(data => console.log(data));
Note: Don't rely on third-party CORS proxies in production!
Platform-Specific Guides
Detailed implementation instructions for your specific platform:
| Platform | Troubleshooting Guide |
|---|---|
| Shopify | Shopify CORS Guide |
| WordPress | WordPress CORS Guide |
| Wix | Wix CORS Guide |
| Squarespace | Squarespace CORS Guide |
| Webflow | Webflow CORS Guide |
Verification
After fixing CORS issues:
Browser console:
- No CORS errors
- Requests succeed
- Data loads properly
Network tab:
- 200 OK status
- CORS headers present
- OPTIONS requests succeed
Test all scenarios:
- Simple GET requests
- POST with JSON
- Requests with credentials
- Custom headers
Test from production domain:
- Not just localhost
- Verify actual domain works
Cross-browser testing:
- Chrome, Firefox, Safari
- Mobile browsers
Common Mistakes
- Using wildcard with credentials - Doesn't work
- Only allowing localhost - Production breaks
- Missing preflight handling - OPTIONS requests fail
- Wrong origin format - Include protocol and port
- Not testing with credentials - Different configuration needed
- Allowing all origins in production - Security risk
- Missing headers on errors - CORS headers needed on all responses
- Case sensitivity - Headers are case-insensitive but origins aren't
- Not caching preflight - Performance impact
- Relying on CORS proxies - Not a production solution