"Request header field X is not allowed by Access-Control-Allow-Headers in preflight response" is CORS's preflight mechanism catching a specific mismatch: your client is sending a custom header the server hasn't explicitly allowlisted, and the browser blocks the actual request before it even fires, based entirely on the server's stated preflight response.
This error means your request includes a header that triggers a CORS preflight (an OPTIONS request sent automatically by the browser before the real request), and the server's response to that preflight didn't include the header you're using in its Access-Control-Allow-Headers list — the browser enforces this strictly, blocking the actual request even though the server might otherwise be perfectly willing to accept it.
Why This Error Happens
Certain request characteristics — custom headers beyond a small standard set, non-simple content types, certain HTTP methods — trigger the browser to send a preflight OPTIONS request first, asking the server which headers, methods, and origins it permits. The actual request only proceeds if the preflight response explicitly allows what the real request is about to send. This error means the server's preflight response's Access-Control-Allow-Headers list doesn't include a header your actual request is sending — commonly a custom auth header, a client version header, or similar application-specific header the server-side CORS configuration hasn't been updated to allow.
Reproducing the Error
A client sending a custom header not reflected in the server's CORS configuration:
fetch("https://api.example.com/data", {
headers: {
"X-Client-Version": "2.4.0", // custom header triggers preflight
},
});
// Preflight OPTIONS request fires; if the server's response doesn't include
// 'X-Client-Version' in Access-Control-Allow-Headers, this error blocks the actual request:
// Request header field X-Client-Version is not allowed by Access-Control-Allow-Headers
The server's CORS configuration missing that specific header:
// Server-side (Express example) — missing the custom header
app.use(cors({
origin: "https://yourapp.com",
allowedHeaders: ["Content-Type", "Authorization"], // X-Client-Version not included
}));
Core Concepts Behind This Error
This is enforced entirely by the browser based on the server's preflight response, meaning the fix is always server-side configuration — no amount of client-side code changes can work around a server that hasn't allowlisted the header you need to send, short of not sending that header at all.
Access-Control-Allow-Headers needs to explicitly list every non-standard header your client sends, not just the ones that seem obviously necessary — adding a new custom header to your client code without a corresponding server-side CORS configuration update is the most common cause of this error appearing after a client-side change.
The preflight response is cached by the browser for a duration controlled by Access-Control-Max-Age, meaning a server-side CORS fix might not take effect immediately in a browser that already cached an older, more restrictive preflight response — worth being aware of when testing a fix and seeing it not immediately resolve.
Wildcard (*) in Access-Control-Allow-Headers is supported by some servers/configurations but has limitations — notably, it doesn't cover the Authorization header in most implementations, which needs to be explicitly listed even when using a wildcard for other headers.
Fixing "Request Header Field Not Allowed by Access-Control-Allow-Headers"
Fix 1: Update the server's CORS configuration to explicitly allow the specific header your client sends:
// Server-side (Express example)
app.use(cors({
origin: "https://yourapp.com",
allowedHeaders: ["Content-Type", "Authorization", "X-Client-Version"],
}));
Fix 2: For a manually-implemented CORS response (not using a library), set the header explicitly on the preflight OPTIONS response:
app.options("*", (req, res) => {
res.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Client-Version");
res.set("Access-Control-Allow-Origin", "https://yourapp.com");
res.sendStatus(204);
});
Fix 3: If the custom header isn't strictly necessary, consider removing it or moving its information into an already-allowed mechanism (like encoding it into the URL or an already-allowed header), reducing your CORS configuration surface, though this is a secondary option to a proper server-side fix when the header genuinely serves a purpose.
Why Does This Error Sometimes Not Go Away Immediately After Fixing the Server?
Because the browser caches preflight responses for the duration set by Access-Control-Max-Age, so a client that already received and cached an older, more restrictive preflight response may continue to enforce that cached result until it expires. Hard-refreshing, clearing the browser cache, or waiting out the max-age duration resolves this — the server-side fix is correct, it just hasn't been re-fetched yet by that specific client.
Preventing This Error in Production
Keep server-side CORS allowedHeaders configuration synchronized with every custom header your client applications actually send, updating it as part of the same change whenever a new custom header is introduced client-side. Set a reasonable Access-Control-Max-Age that balances reducing preflight request overhead against how quickly CORS configuration changes should take effect for already-connected clients.
If you hit this error, check the server's Access-Control-Allow-Headers configuration first against exactly what your client is sending — the fix is virtually always adding the missing header there, and no client-side workaround exists for a genuinely restrictive server configuration.