CORS: Content-Type application/json is not safelisted and triggers a preflight
Your cross-origin request with Content-Type: application/json fails CORS? That value isn't CORS-safelisted, so the browser sends a preflight OPTIONS request your server must answer.
Short answer
Content-Type: application/json is not a CORS-safelisted value, so the browser sends a preflight OPTIONS request before your cross-origin POST. MDN says only application/x-www-form-urlencoded, multipart/form-data and text/plain are safelisted. Fix it on the server: answer OPTIONS with Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers that includes Content-Type.
Reviewed October 28, 2026
What it means in plain English
Browsers send some cross-origin requests straight away and check others first. MDN calls the first kind simple requests. One condition is that Content-Type has one of three values: application/x-www-form-urlencoded, multipart/form-data, or text/plain. MDN's glossary lists Content-Type as a CORS-safelisted request header only with those values.
application/json is not on that list. So a fetch that sends JSON to another origin is preflighted. MDN: "the browser first sends an HTTP request using the OPTIONS method to the resource on the other origin, in order to determine if the actual request is safe to send." The preflight carries Access-Control-Request-Method and Access-Control-Request-Headers: content-type. If the server doesn't approve them, the real request is never sent, and the console shows a CORS error.
Likely causes
- The server or function doesn't handle
OPTIONSat all, or returns an error for it. - The server answers
OPTIONSbut itsAccess-Control-Allow-Headersdoesn't includeContent-Type. - The request is redirected after the preflight. MDN says some browsers report "Request requires preflight, which is disallowed to follow cross-origin redirects."
- The request sends cookies and the response uses
Access-Control-Allow-Origin: *. MDN says the browser blocks that.
How to fix it, step by step
- Open the network panel, find the
OPTIONSrequest just before the failing call, and read its status and response headers. - If you control the server, make it answer
OPTIONSwith a success status and these headers, following MDN's example:
Access-Control-Allow-Origin: https://yourapp.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
Add any other request headers you send, such as Authorization, to Access-Control-Allow-Headers.
3. Send Access-Control-Allow-Origin on the real response too, not only on the preflight.
4. Call the final URL directly so there is no redirect after the preflight.
5. If you don't control the server, call it from your own backend instead of the browser. See Blocked by CORS policy.
6. For a Supabase Edge Function, the function must handle OPTIONS itself. See Supabase Edge Function CORS error.
How to check it worked
- The
OPTIONSrequest returns a success status withAccess-Control-Allow-Headersthat includesContent-Type. - The real request follows it and returns your data, with no CORS error in the console.
Questions people ask
Why does application/json trigger a CORS preflight?
MDN lists Content-Type as safelisted only when its value is application/x-www-form-urlencoded, multipart/form-data or text/plain. Any other value, including application/json, makes the request one the browser preflights.
What should the server send back for the CORS preflight?
A success response to the OPTIONS request that allows your origin, the method and the Content-Type header, using Access-Control-Allow-Origin, Access-Control-Allow-Methods and Access-Control-Allow-Headers.
Can the browser cache the CORS preflight?
Yes. The server can send Access-Control-Max-Age, and MDN says preflight responses are kept in a cache separate from the browser's general HTTP cache.
Sources
Related fixes
More Supabase Edge Functions fixes
More Network errors in production fixes
Get the 10-point launch-risk checklist for vibe-coded apps
For apps built with Lovable, Bolt, Cursor or Replit. Built from the real failures on our fix pages, plus What broke this week: one short email with that week's errors and the fix in plain English.
Keep going on your own.
The guides stay free. A deep leak report is $15. Fix packs are $24, or $49 for all three. There is no done-for-you repair.