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 OPTIONS at all, or returns an error for it.
  • The server answers OPTIONS but its Access-Control-Allow-Headers doesn't include Content-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

  1. Open the network panel, find the OPTIONS request just before the failing call, and read its status and response headers.
  2. If you control the server, make it answer OPTIONS with 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 OPTIONS request returns a success status with Access-Control-Allow-Headers that includes Content-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

More Supabase Edge Functions fixes

More Network errors in production fixes

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.

See products