CORS Errors — What They Mean and How to Fix Them
Step-by-step solutions for Cross-Origin Resource Sharing problems — server configs for Express, Next.js, Apache, Nginx
Browser Blocked — Client Side
Client No 'Access-Control-Allow-Origin' header
The server's response does not include the CORS header that authorizes your origin. The browser blocks the response.
Access-Control-Allow-Origin header to the response. See server examples below.If you DON'T control the server: Options:
- Use a backend proxy (your server calls the API, adds CORS headers, your frontend calls your server)
- Ask the API provider to enable CORS for your origin
- For development only: disable browser CORS checks (not production-safe)
Client Response to preflight request doesn't pass access control
The browser sent an OPTIONS preflight request (because your request has custom headers or non-simple method). The server responded with non-200 status or missing CORS headers for OPTIONS.
- 1Ensure your server handles OPTIONS requests for that endpoint:
// Express example — before your route handler
app.options('/api/data', cors()); // preflight responder- 2OR configure CORS middleware to handle preflight automatically (see below).
Authorization, X-), or Content-Type other than application/x-www-form-urlencoded, multipart/form-data, text/plain.Client Credentials flag is 'true', but 'Access-Control-Allow-Credentials' is ''
Your frontend sets withCredentials: true or credentials: 'include' (cookies, auth headers), but the server did not include Access-Control-Allow-Credentials: true in its response.
- 1On the server, add this header:
Access-Control-Allow-Credentials: true- 2Important: When credentials are true,
Access-Control-Allow-Origincannot be*. You must echo the specific origin:
Access-Control-Allow-Origin: https://myapp.comAccess-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true. It's invalid and will be rejected by browsers.Client Request header field X- is not allowed
Your request includes a header that the server hasn't explicitly allowed in its Access-Control-Allow-Headers response header.
- 1On server, add the required header name to
Access-Control-Allow-Headers:
Access-Control-Allow-Headers: Authorization, X-Custom-Header, Content-TypeAuthorization, X-Requested-With, X-CSRF-Token. Access-Control-Allow-Headers: * is valid in Fetch spec but not universally supported; list explicitly for compatibility.Client Method PUT/PATCH/DELETE is not allowed
Your request uses a method not listed in the server's Access-Control-Allow-Methods header in the preflight response.
- 1On server, include the method in
Access-Control-Allow-Methods:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, PATCHOPTIONS must be allowed because the browser sends an OPTIONS preflight request before the actual method.Server-Side Fixes
Server Dynamic origin — allow multiple domains
You want to allow several specific origins (not *). Echo back the Origin request header if it's in your whitelist.
// Express.js
const allowed = ["https://myapp.com", "https://admin.myapp.com"];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowed.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));// Next.js — middleware.js
export function middleware(request) {
const response = NextResponse.next();
const origin = request.headers.get('origin');
const allowed = ["https://myapp.com", "https://app.myapp.com"];
if (allowed.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
response.headers.set('Access-Control-Allow-Credentials', 'true');
}
return response;
}!origin check allows same-origin requests (mobile apps, Postman) that send no Origin header.Server Apache .htaccess CORS configuration
# .htaccess in document root
<IfModule mod_headers.c>
Header always set Access-Control-Allow-Origin "https://myapp.com"
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
Header always set Access-Control-Allow-Credentials "true"
</IfModule># Handle OPTIONS preflight
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]mod_headers enabled (a2enmod headers). For dynamic origin, need mod_setenvif or server-side scripting (PHP/Python).Server Nginx CORS configuration
# nginx.conf or site config
location /api/ {
add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
# Preflight
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}always ensures headers are set even on error responses. Access-Control-Max-Age (seconds) caches preflight in browser. 1728000 = 20 days.Server Cloudflare Worker CORS
export default {
async fetch(request, env, ctx) {
const response = await fetch(request);
const newHeaders = new Headers(response.headers);
newHeaders.set('Access-Control-Allow-Origin', 'https://myapp.com');
newHeaders.set('Access-Control-Allow-Credentials', 'true');
return new Response(response.body, {
status: response.status,
headers: newHeaders
});
}
};Common Pitfalls
The Wildcard — Credentials Trap
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: trueForbidden — browsers reject this combination.
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Credentials: trueSpecific origin required when credentials = true.
Localhost Origins
Allow
http://localhost:3000, http://127.0.0.1:5173, etc.Access-Control-Allow-Origin: http://localhost:3000
Switch to your real domain:
https://myapp.comUse env variables to differ dev/prod config.
Pitfall CORS headers not preserved on redirects
CORS validation is per-response. A redirect response must itself have the appropriate Access-Control-Allow-Origin header if it's cross-origin, but usually redirects don't include CORS headers.
- 1Update the client to call the final URL directly (avoid redirect).
- 2Or ensure the redirect response includes
Access-Control-Allow-Origin(rarely practical).
CORS: Why It Exists and How to Fix It
CORS (Cross-Origin Resource Sharing) is a browser security mechanism that blocks JavaScript from making requests to a different origin than the page it's running on. It's not a bug — it's the browser enforcing the Same-Origin Policy to prevent malicious scripts on one site from reading data from another. The fix always lives on the server, not the client: the server needs to return the right Access-Control-Allow-Origin header.
The credentials trap catches a lot of developers: you cannot use Access-Control-Allow-Origin: * when sending cookies or Authorization headers. The browser blocks it. The correct approach is to echo back the specific requesting origin dynamically and set Access-Control-Allow-Credentials: true. This guide shows the exact headers and server-side snippets for the most common frameworks.
Many CORS problems are best solved at the CDN layer. Cloudflare Workers and AWS CloudFront Functions can inject the correct headers before a request even reaches your application server — centralizing policy management and eliminating the issue for every route at once.
Complete Developer Toolkit
CORS errors are fundamentally about your browser blocking cross-origin API requests. While fixing them requires server-side changes, diagnosing them involves several other tools. Use our DNS lookup to confirm both your frontend and API domains are resolving to the correct servers — a misconfigured DNS can make requests go to unexpected origins. Our SSL expiry checker verifies your API's HTTPS certificate is valid, since browsers also block requests to origins with invalid certificates, producing errors that can look like CORS issues.
Our API response simulator lets you mock API responses locally so you can test your frontend without hitting a CORS-restricted endpoint during development. When your API returns JSON, use our JSON formatter to validate the response structure. Our URL encoder encodes origin URLs and query parameters correctly. The JWT decoder inspects Authorization tokens your API requires — a misconfigured auth header is a common trigger for CORS preflight failures. Our regex tester helps write patterns to validate the origin whitelist you configure on your server. The Core Web Vitals guide explains how CORS-blocked resources indirectly affect LCP scores.