Chrome 76/77中XMLHttpRequest的Sec-Fetch-Mode引发CORS请求400错误排查
Alright, let's break this down step by step—this looks like a compatibility bug specific to Chrome 76/77's early implementation of Fetch Metadata Headers, not a standard security restriction. Here's why, and how to fix it from both server and client sides:
Root Cause Analysis
First, a key clarification: XMLHttpRequest doesn’t have a "no-cors" mode—that’s a Fetch API-specific concept. Chrome 76/77 was the first wave of browsers rolling out Sec-Fetch-* headers, and its logic for attaching these headers to XHR requests was flawed. When you sent a cross-site XHR request, Chrome incorrectly stamped it with Sec-Fetch-Mode: no-cors (a mode that shouldn’t apply to XHR), which your C# Web API’s existing CORS logic wasn’t prepared to handle—hence the 400 Bad Request.
Your working scenario (Sec-Fetch-Mode: cors + same-site) works because same-site requests don’t trigger the same cross-origin checks, and Chrome attaches the correct cors mode header that your API already handles.
Server-Side Fixes (C# Web API)
These changes will make your API compatible with Chrome’s buggy header behavior in older versions:
Update CORS policy to allow
Sec-Fetch-Modeheaders:
Ensure your CORS configuration explicitly permits theSec-Fetch-Modeheader, so it doesn’t get rejected during preflight or request processing. Example code inProgram.cs(orStartup.csfor older .NET versions):services.AddCors(options => { options.AddPolicy("AllowCrossOrigin", builder => { builder.AllowAnyOrigin() // Adjust to your allowed origins in production .AllowAnyMethod() .AllowAnyHeader() .WithHeaders("Sec-Fetch-Mode"); // Explicitly allow this header }); }); // Don't forget to enable CORS middleware app.UseCors("AllowCrossOrigin");Debug the exact 400 error cause:
Enable detailed error logging in your Web API to see what’s triggering the 400. You can add logging configuration to capture response details, or use a tool like Fiddler/Charles to inspect the server’s full error response. Common triggers include preflight OPTIONS requests being rejected, or custom middleware blocking requests with unexpected headers.Loosen preflight request checks:
If your API has custom middleware validating preflight headers, add an exception for requests withSec-Fetch-Mode: no-cors—since this is a Chrome-specific bug, your API shouldn’t reject valid cross-origin requests over this header.
Client-Side Fixes
If you can’t modify the server (or want a client-side workaround):
Ensure XHR requests follow "simple request" rules:
Simple requests (GET/POST with only allowed headers:Accept,Accept-Language,Content-Language,Content-Typerestricted toapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain) don’t trigger preflight OPTIONS requests. This can bypass Chrome’s incorrect header stamping behavior. Example XHR setup:const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://your-api-endpoint.com', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('key1=value1&key2=value2');Upgrade Chrome to a newer version:
Chrome 76/77 are outdated (released in 2019), and later versions (78+) fixed most of the early Fetch Metadata Header bugs. Encouraging users to update their browser is the most permanent fix for this issue.Switch to Fetch API (if feasible):
If you can refactor your request logic to use the Fetch API, explicitly setmode: 'cors'to ensure Chrome sends the correct header. This avoids the XHR-specific bug entirely:fetch('https://your-api-endpoint.com', { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'key1=value1&key2=value2' });
内容的提问来源于stack exchange,提问作者Benjamin E.

